Merge branch 'release/os/4.5' into jamesh/error-reporting-sync-29-04-20

# Conflicts:
#	node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt
This commit is contained in:
James Higgs 2020-04-29 11:47:14 +01:00
commit adbe030a2c
720 changed files with 10144 additions and 38638 deletions

View File

@ -0,0 +1,83 @@
import static com.r3.build.BuildControl.killAllExistingBuildsForJob
@Library('corda-shared-build-pipeline-steps')
import static com.r3.build.BuildControl.killAllExistingBuildsForJob
killAllExistingBuildsForJob(env.JOB_NAME, env.BUILD_NUMBER.toInteger())
pipeline {
agent { label 'k8s' }
options {
timestamps()
timeout(time: 3, unit: 'HOURS')
}
environment {
DOCKER_TAG_TO_USE = "${env.GIT_COMMIT.subSequence(0, 8)}"
EXECUTOR_NUMBER = "${env.EXECUTOR_NUMBER}"
BUILD_ID = "${env.BUILD_ID}-${env.JOB_NAME}"
ARTIFACTORY_CREDENTIALS = credentials('artifactory-credentials')
}
stages {
stage('Corda Pull Request - Generate Build Image') {
steps {
withCredentials([string(credentialsId: 'container_reg_passwd', variable: 'DOCKER_PUSH_PWD')]) {
sh "./gradlew --no-daemon " +
"-Dkubenetize=true " +
"-Ddocker.push.password=\"\${DOCKER_PUSH_PWD}\" " +
"-Ddocker.work.dir=\"/tmp/\${EXECUTOR_NUMBER}\" " +
"-Ddocker.build.tag=\"\${DOCKER_TAG_TO_USE}\" " +
"-Ddocker.buildbase.tag=11latest " +
"-Ddocker.dockerfile=DockerfileJDK11Azul" +
" clean pushBuildImage --stacktrace"
}
sh "kubectl auth can-i get pods"
}
}
stage('Corda Pull Request - Run Tests') {
parallel {
stage('Integration Tests') {
steps {
sh "./gradlew --no-daemon " +
"-DbuildId=\"\${BUILD_ID}\" " +
"-Dkubenetize=true " +
"-Ddocker.run.tag=\"\${DOCKER_TAG_TO_USE}\" " +
"-Dartifactory.username=\"\${ARTIFACTORY_CREDENTIALS_USR}\" " +
"-Dartifactory.password=\"\${ARTIFACTORY_CREDENTIALS_PSW}\" " +
"-Dgit.branch=\"\${GIT_BRANCH}\" " +
"-Dgit.target.branch=\"\${CHANGE_TARGET}\" " +
"-Ddependx.branch.origin=${env.GIT_COMMIT} " +
"-Ddependx.branch.target=${CHANGE_TARGET} " +
" allParallelIntegrationTest --stacktrace"
}
}
stage('Unit Tests') {
steps {
sh "./gradlew --no-daemon " +
"-DbuildId=\"\${BUILD_ID}\" " +
"-Dkubenetize=true " +
"-Ddocker.run.tag=\"\${DOCKER_TAG_TO_USE}\" " +
"-Dartifactory.username=\"\${ARTIFACTORY_CREDENTIALS_USR}\" " +
"-Dartifactory.password=\"\${ARTIFACTORY_CREDENTIALS_PSW}\" " +
"-Dgit.branch=\"\${GIT_BRANCH}\" " +
"-Dgit.target.branch=\"\${CHANGE_TARGET}\" " +
"-Ddependx.branch.origin=${env.GIT_COMMIT} " +
"-Ddependx.branch.target=${CHANGE_TARGET} " +
" allParallelUnitTest --stacktrace"
}
}
}
}
}
post {
always {
archiveArtifacts artifacts: '**/pod-logs/**/*.log', fingerprint: false
junit '**/build/test-results-xml/**/*.xml'
}
cleanup {
deleteDir() /* clean up our workspace */
}
}
}

View File

@ -5,7 +5,7 @@ import static com.r3.build.BuildControl.killAllExistingBuildsForJob
killAllExistingBuildsForJob(env.JOB_NAME, env.BUILD_NUMBER.toInteger()) killAllExistingBuildsForJob(env.JOB_NAME, env.BUILD_NUMBER.toInteger())
pipeline { pipeline {
agent { label 'local-k8s' } agent { label 'k8s' }
options { options {
timestamps() timestamps()
timeout(time: 3, unit: 'HOURS') timeout(time: 3, unit: 'HOURS')

View File

@ -4,7 +4,7 @@ import static com.r3.build.BuildControl.killAllExistingBuildsForJob
killAllExistingBuildsForJob(env.JOB_NAME, env.BUILD_NUMBER.toInteger()) killAllExistingBuildsForJob(env.JOB_NAME, env.BUILD_NUMBER.toInteger())
pipeline { pipeline {
agent { label 'local-k8s' } agent { label 'k8s' }
options { options {
timestamps() timestamps()
overrideIndexTriggers(false) overrideIndexTriggers(false)

View File

@ -3,4 +3,4 @@ import static com.r3.build.BuildControl.killAllExistingBuildsForJob
killAllExistingBuildsForJob(env.JOB_NAME, env.BUILD_NUMBER.toInteger()) killAllExistingBuildsForJob(env.JOB_NAME, env.BUILD_NUMBER.toInteger())
onDemandTestPipeline('local-k8s', '.ci/dev/on-demand-tests/commentMappings.yml') onDemandTestPipeline('k8s', '.ci/dev/on-demand-tests/commentMappings.yml')

View File

@ -4,7 +4,7 @@ import static com.r3.build.BuildControl.killAllExistingBuildsForJob
killAllExistingBuildsForJob(env.JOB_NAME, env.BUILD_NUMBER.toInteger()) killAllExistingBuildsForJob(env.JOB_NAME, env.BUILD_NUMBER.toInteger())
pipeline { pipeline {
agent { label 'local-k8s' } agent { label 'k8s' }
options { options {
timestamps() timestamps()
buildDiscarder(logRotator(daysToKeepStr: '7', artifactDaysToKeepStr: '7')) buildDiscarder(logRotator(daysToKeepStr: '7', artifactDaysToKeepStr: '7'))
@ -61,6 +61,36 @@ pipeline {
" allParallelSlowIntegrationTest --stacktrace" " allParallelSlowIntegrationTest --stacktrace"
} }
} }
stage('Generate sonarqube report') {
steps {
script {
try {
// running this step here is the only way to not majorly affect the distributed test plugin,
// as now that neither returns build files nor runs jacoco reports
sh "./gradlew --no-daemon build jacocoRootReport --stacktrace"
withSonarQubeEnv('sq01') {
sh "./gradlew --no-daemon sonarqube -x test --stacktrace"
}
timeout(time: 3, unit: 'MINUTES') {
script {
try {
def qg = waitForQualityGate();
if (qg.status != 'OK') {
error "Pipeline aborted due to quality gate failure: ${qg.status}"
}
} catch (org.jenkinsci.plugins.workflow.steps.FlowInterruptedException e) {
println('No sonarqube webhook response within timeout. Please check the webhook configuration in sonarqube.')
// continue the pipeline
}
}
}
} catch (err) {
println('Error while trying to execute sonarqube analysis, will be skipped.')
}
}
}
}
} }
} }
} }

View File

@ -4,7 +4,7 @@ import static com.r3.build.BuildControl.killAllExistingBuildsForJob
killAllExistingBuildsForJob(env.JOB_NAME, env.BUILD_NUMBER.toInteger()) killAllExistingBuildsForJob(env.JOB_NAME, env.BUILD_NUMBER.toInteger())
pipeline { pipeline {
agent { label 'local-k8s' } agent { label 'k8s' }
options { options {
timestamps() timestamps()
overrideIndexTriggers(false) overrideIndexTriggers(false)

View File

@ -5,7 +5,7 @@ import static com.r3.build.BuildControl.killAllExistingBuildsForJob
killAllExistingBuildsForJob(env.JOB_NAME, env.BUILD_NUMBER.toInteger()) killAllExistingBuildsForJob(env.JOB_NAME, env.BUILD_NUMBER.toInteger())
pipeline { pipeline {
agent { label 'local-k8s' } agent { label 'k8s' }
options { options {
timestamps() timestamps()
timeout(time: 3, unit: 'HOURS') timeout(time: 3, unit: 'HOURS')

19
.github/workflows/jira_assign_issue.yml vendored Normal file
View File

@ -0,0 +1,19 @@
name: Sync assigned jira issues
on:
schedule:
- cron: '15 * * * *'
jobs:
sync_assigned:
runs-on: ubuntu-latest
steps:
- name: Assign
uses: corda/jira-sync-assigned-action@master
with:
jiraBaseUrl: ${{ secrets.JIRA_BASE_URL }}
jiraEmail: ${{ secrets.JIRA_USER_EMAIL }}
jiraToken: ${{ secrets.JIRA_API_TOKEN }}
token: ${{ secrets.GH_TOKEN }}
owner: corda
repository: corda

20
.github/workflows/jira_close_issue.yml vendored Normal file
View File

@ -0,0 +1,20 @@
name: Sync closed jira issues
on:
schedule:
- cron: '30 * * * *'
jobs:
sync_closed:
runs-on: ubuntu-latest
steps:
- name: Close
uses: corda/jira-sync-closed-action@master
with:
project: CORDA
jiraBaseUrl: https://r3-cev.atlassian.net
jiraEmail: ${{ secrets.JIRA_USER_EMAIL }}
jiraToken: ${{ secrets.JIRA_API_TOKEN }}
token: ${{ secrets.GH_TOKEN }}
owner: corda
repository: corda

36
.github/workflows/jira_create_issue.yml vendored Normal file
View File

@ -0,0 +1,36 @@
name: Create jira issue from github issue
on:
issues:
types: [opened]
jobs:
update_jira:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Jira Create issue
id: create
uses: corda/jira-create-issue-action@master
with:
jiraBaseUrl: https://r3-cev.atlassian.net
project: CORDA
issuetype: Bug
summary: ${{ github.event.issue.title }}
labels: community
jiraEmail: ${{ secrets.JIRA_USER_EMAIL }}
jiraToken: ${{ secrets.JIRA_API_TOKEN }}
description: |
${{ github.event.issue.body }}
Created by github action.
- name: Create comment
uses: peter-evans/create-or-update-comment@v1
with:
token: ${{ secrets.GH_TOKEN }}
issue-number: ${{ github.event.issue.number }}
body: |
Automatically created Jira issue: ${{ steps.create.outputs.issue }}
reaction-type: '+1'

2
Jenkinsfile vendored
View File

@ -5,7 +5,7 @@ import static com.r3.build.BuildControl.killAllExistingBuildsForJob
killAllExistingBuildsForJob(env.JOB_NAME, env.BUILD_NUMBER.toInteger()) killAllExistingBuildsForJob(env.JOB_NAME, env.BUILD_NUMBER.toInteger())
pipeline { pipeline {
agent { label 'local-k8s' } agent { label 'k8s' }
options { options {
timestamps() timestamps()
timeout(time: 3, unit: 'HOURS') timeout(time: 3, unit: 'HOURS')

View File

@ -2,7 +2,7 @@
<img src="https://www.corda.net/wp-content/themes/corda/assets/images/crda-logo-big.svg" alt="Corda" width="500"> <img src="https://www.corda.net/wp-content/themes/corda/assets/images/crda-logo-big.svg" alt="Corda" width="500">
</p> </p>
<a href="https://ci-master.corda.r3cev.com/viewType.html?buildTypeId=Corda_CordaBuild&tab=buildTypeStatusDiv&guest=1"><img src="https://ci.corda.r3cev.com/app/rest/builds/buildType:Corda_CordaBuild/statusIcon"/></a> [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) <a href="https://ci-master.corda.r3cev.com/viewType.html?buildTypeId=Corda_Build_ActiveReleaseBranches_BuildOsRelease45&tab=buildTypeStatusDiv&guest=1"><img src="https://ci.corda.r3cev.com/app/rest/builds/buildType:Corda_Build_ActiveReleaseBranches_BuildOsRelease45/statusIcon"/></a> [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
# Corda # Corda

View File

@ -79,8 +79,8 @@ buildscript {
ext.djvm_version = constants.getProperty("djvmVersion") ext.djvm_version = constants.getProperty("djvmVersion")
ext.deterministic_rt_version = constants.getProperty('deterministicRtVersion') ext.deterministic_rt_version = constants.getProperty('deterministicRtVersion')
ext.okhttp_version = '3.14.2' ext.okhttp_version = '3.14.2'
ext.netty_version = '4.1.29.Final' ext.netty_version = '4.1.46.Final'
ext.tcnative_version = '2.0.14.Final' ext.tcnative_version = '2.0.29.Final'
ext.typesafe_config_version = constants.getProperty("typesafeConfigVersion") ext.typesafe_config_version = constants.getProperty("typesafeConfigVersion")
ext.fileupload_version = '1.4' ext.fileupload_version = '1.4'
ext.kryo_version = '4.0.2' ext.kryo_version = '4.0.2'
@ -187,8 +187,9 @@ buildscript {
// See https://github.com/corda/gradle-capsule-plugin // See https://github.com/corda/gradle-capsule-plugin
classpath "us.kirchmeier:gradle-capsule-plugin:1.0.4_r3" classpath "us.kirchmeier:gradle-capsule-plugin:1.0.4_r3"
classpath group: "com.r3.testing", name: "gradle-distributed-testing-plugin", version: "1.2-LOCAL-K8S-SHARED-CACHE-SNAPSHOT", changing: true classpath group: "com.r3.testing", name: "gradle-distributed-testing-plugin", version: "1.2-LOCAL-K8S-SHARED-CACHE-SNAPSHOT", changing: true
classpath group: "com.r3.dependx", name: "gradle-dependx", version: "0.1.12", changing: true classpath group: "com.r3.dependx", name: "gradle-dependx", version: "0.1.13", changing: true
classpath "com.bmuschko:gradle-docker-plugin:5.0.0" classpath "com.bmuschko:gradle-docker-plugin:5.0.0"
classpath "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.8"
} }
} }
@ -241,6 +242,7 @@ allprojects {
apply plugin: 'jacoco' apply plugin: 'jacoco'
apply plugin: 'org.owasp.dependencycheck' apply plugin: 'org.owasp.dependencycheck'
apply plugin: 'kotlin-allopen' apply plugin: 'kotlin-allopen'
apply plugin: 'org.sonarqube'
allOpen { allOpen {
annotations( annotations(
@ -400,6 +402,16 @@ allprojects {
} }
} }
} }
sonarqube {
properties {
property "sonar.projectName", "Corda"
property "sonar.projectKey", "corda"
property 'sonar.tests', '**/src/test/**,**/src/smoke-test/**,**/src/integration-test/**,**/src/integration-test-slow/**'
property 'sonar.coverage.jacoco.xmlReportPaths', "${rootDir.path}/build/reports/jacoco/jacocoRootReport/jacocoRootReport.xml"
property 'detekt.sonar.kotlin.baseline.path', "${rootDir.path}/detekt-baseline.xml"
property 'detekt.sonar.kotlin.config.path', "${rootDir.path}/detekt-config.yml"
}
}
// Check that we are running on a Java 8 JDK. The source/targetCompatibility values above aren't sufficient to // Check that we are running on a Java 8 JDK. The source/targetCompatibility values above aren't sufficient to
// guarantee this because those are properties checked by the Java plugin, but we're using Kotlin. // guarantee this because those are properties checked by the Java plugin, but we're using Kotlin.
@ -459,6 +471,28 @@ task jacocoRootReport(type: org.gradle.testing.jacoco.tasks.JacocoReport) {
it.exists() it.exists()
}) })
} }
afterEvaluate {
classDirectories = files(classDirectories.files.collect {
fileTree(dir: it,
// these exclusions are necessary because jacoco gets confused by same class names
// which occur due to deterministic versions of non deterministic classes
exclude: ['**/net/corda/core/crypto/SHA256DigestSupplier**',
'**/net/corda/core/crypto/DelegatingSecureRandomService',
'**/net/corda/core/internal/ThreadLocalToggleField**',
'**/net/corda/core/internal/InheritableThreadLocalToggleField**',
'**/net/corda/core/internal/ToggleField**',
'net/corda/core/internal/rules/StateContractValidationEnforcementRule**',
'net/corda/core/internal/SimpleToggleField**',
'net/corda/core/serialization/SerializationFactory**',
'net/corda/serialization/internal/amqp/AMQPStreams**',
'net/corda/serialization/internal/amqp/AMQPSerializerFactories**',
'net/corda/serialization/internal/amqp/AMQPSerializationThreadContext**',
'net/corda/serialization/internal/ByteBufferStreams**',
'net/corda/serialization/internal/model/DefaultCacheProvider**',
'net/corda/serialization/internal/DefaultWhitelist**'
])
})
}
} }
tasks.register('detekt', JavaExec) { tasks.register('detekt', JavaExec) {
@ -635,67 +669,83 @@ buildScan {
termsOfServiceAgree = 'yes' termsOfServiceAgree = 'yes'
} }
ext.generalPurpose = [
numberOfShards: 10,
streamOutput: false,
coresPerFork: 2,
memoryInGbPerFork: 12,
nodeTaints: "small"
]
ext.largeScaleSet = [
numberOfShards: 15,
streamOutput: false,
coresPerFork: 6,
memoryInGbPerFork: 10,
nodeTaints: "big"
]
task allParallelIntegrationTest(type: ParallelTestGroup) { task allParallelIntegrationTest(type: ParallelTestGroup) {
dependsOn dependxiesModule dependsOn dependxiesModule
podLogLevel PodLogLevel.INFO podLogLevel PodLogLevel.INFO
testGroups "integrationTest" testGroups "integrationTest"
numberOfShards 10 numberOfShards generalPurpose.numberOfShards
streamOutput false streamOutput generalPurpose.streamOutput
coresPerFork 2 coresPerFork generalPurpose.coresPerFork
memoryInGbPerFork 12 memoryInGbPerFork generalPurpose.memoryInGbPerFork
nodeTaints generalPurpose.nodeTaints
distribute DistributeTestsBy.METHOD distribute DistributeTestsBy.METHOD
nodeTaints "big"
} }
task allParallelUnitTest(type: ParallelTestGroup) { task allParallelUnitTest(type: ParallelTestGroup) {
dependsOn dependxiesModule dependsOn dependxiesModule
podLogLevel PodLogLevel.INFO podLogLevel PodLogLevel.INFO
testGroups "test" testGroups "test"
numberOfShards 10 numberOfShards generalPurpose.numberOfShards
streamOutput false streamOutput generalPurpose.streamOutput
coresPerFork 2 coresPerFork generalPurpose.coresPerFork
memoryInGbPerFork 12 memoryInGbPerFork generalPurpose.memoryInGbPerFork
nodeTaints generalPurpose.nodeTaints
distribute DistributeTestsBy.CLASS distribute DistributeTestsBy.CLASS
nodeTaints "small"
} }
task allParallelUnitAndIntegrationTest(type: ParallelTestGroup) { task allParallelUnitAndIntegrationTest(type: ParallelTestGroup) {
dependsOn dependxiesModule dependsOn dependxiesModule
testGroups "test", "integrationTest" testGroups "test", "integrationTest"
numberOfShards 15 numberOfShards generalPurpose.numberOfShards
streamOutput false streamOutput generalPurpose.streamOutput
coresPerFork 6 coresPerFork generalPurpose.coresPerFork
memoryInGbPerFork 10 memoryInGbPerFork generalPurpose.memoryInGbPerFork
nodeTaints generalPurpose.nodeTaints
distribute DistributeTestsBy.METHOD distribute DistributeTestsBy.METHOD
nodeTaints "big"
} }
task parallelRegressionTest(type: ParallelTestGroup) { task parallelRegressionTest(type: ParallelTestGroup) {
testGroups "test", "integrationTest", "smokeTest" testGroups "test", "integrationTest", "smokeTest"
dependsOn dependxiesModule dependsOn dependxiesModule
numberOfShards 15 numberOfShards generalPurpose.numberOfShards
streamOutput false streamOutput generalPurpose.streamOutput
coresPerFork 2 coresPerFork generalPurpose.coresPerFork
memoryInGbPerFork 10 memoryInGbPerFork generalPurpose.memoryInGbPerFork
nodeTaints generalPurpose.nodeTaints
distribute DistributeTestsBy.METHOD distribute DistributeTestsBy.METHOD
nodeTaints "big"
} }
task allParallelSmokeTest(type: ParallelTestGroup) { task allParallelSmokeTest(type: ParallelTestGroup) {
testGroups "smokeTest" testGroups "smokeTest"
dependsOn dependxiesModule dependsOn dependxiesModule
numberOfShards 4 numberOfShards generalPurpose.numberOfShards
streamOutput false streamOutput generalPurpose.streamOutput
coresPerFork 6 coresPerFork generalPurpose.coresPerFork
memoryInGbPerFork 10 memoryInGbPerFork generalPurpose.memoryInGbPerFork
distribute DistributeTestsBy.CLASS nodeTaints generalPurpose.nodeTaints
nodeTaints "big" distribute DistributeTestsBy.METHOD
} }
task allParallelSlowIntegrationTest(type: ParallelTestGroup) { task allParallelSlowIntegrationTest(type: ParallelTestGroup) {
testGroups "slowIntegrationTest" testGroups "slowIntegrationTest"
dependsOn dependxiesModule dependsOn dependxiesModule
numberOfShards 4 numberOfShards generalPurpose.numberOfShards
streamOutput false streamOutput generalPurpose.streamOutput
coresPerFork 6 coresPerFork generalPurpose.coresPerFork
memoryInGbPerFork 10 memoryInGbPerFork generalPurpose.memoryInGbPerFork
distribute DistributeTestsBy.CLASS nodeTaints generalPurpose.nodeTaints
nodeTaints "big" distribute DistributeTestsBy.METHOD
} }
apply plugin: 'com.r3.testing.distributed-testing' apply plugin: 'com.r3.testing.distributed-testing'
apply plugin: 'com.r3.testing.image-building' apply plugin: 'com.r3.testing.image-building'

View File

@ -1,7 +1,6 @@
package net.corda.client.rpc package net.corda.client.rpc
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable
import com.esotericsoftware.kryo.KryoException
import net.corda.core.flows.FlowLogic import net.corda.core.flows.FlowLogic
import net.corda.core.flows.StartableByRPC import net.corda.core.flows.StartableByRPC
import net.corda.core.messaging.startFlow import net.corda.core.messaging.startFlow

View File

@ -14,13 +14,13 @@ import net.corda.core.utilities.Try
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.millis import net.corda.core.utilities.millis
import net.corda.core.utilities.seconds import net.corda.core.utilities.seconds
import net.corda.coretesting.internal.testThreadFactory
import net.corda.node.services.rpc.RPCServerConfiguration import net.corda.node.services.rpc.RPCServerConfiguration
import net.corda.nodeapi.RPCApi import net.corda.nodeapi.RPCApi
import net.corda.testing.common.internal.eventually import net.corda.testing.common.internal.eventually
import net.corda.testing.common.internal.succeeds import net.corda.testing.common.internal.succeeds
import net.corda.testing.core.SerializationEnvironmentRule import net.corda.testing.core.SerializationEnvironmentRule
import net.corda.testing.driver.internal.incrementalPortAllocation import net.corda.testing.driver.internal.incrementalPortAllocation
import net.corda.coretesting.internal.testThreadFactory
import net.corda.testing.node.internal.RPCDriverDSL import net.corda.testing.node.internal.RPCDriverDSL
import net.corda.testing.node.internal.RpcBrokerHandle import net.corda.testing.node.internal.RpcBrokerHandle
import net.corda.testing.node.internal.RpcServerHandle import net.corda.testing.node.internal.RpcServerHandle
@ -75,7 +75,7 @@ class RPCStabilityTests {
values.poll() values.poll()
} }
val first = values.peek() val first = values.peek()
if (values.size == 5 && values.all { it.keys.size == first.keys.size }) { if (values.size == 5 && values.all { it.keys == first.keys }) {
first first
} else { } else {
null null

View File

@ -21,7 +21,7 @@ object Configuration {
/** /**
* Describes a [Config] hiding sensitive data. * Describes a [Config] hiding sensitive data.
*/ */
fun describe(configuration: Config, serialiseValue: (Any?) -> ConfigValue = { value -> ConfigValueFactory.fromAnyRef(value.toString()) }): ConfigValue? fun describe(configuration: Config, serialiseValue: (Any?) -> ConfigValue = { value -> ConfigValueFactory.fromAnyRef(value.toString()) }, options: Options): ConfigValue?
} }
object Value { object Value {
@ -36,10 +36,11 @@ object Configuration {
* *
* @throws ConfigException.Missing if the [Config] does not specify the value. * @throws ConfigException.Missing if the [Config] does not specify the value.
* @throws ConfigException.WrongType if the [Config] specifies a value of the wrong type. * @throws ConfigException.WrongType if the [Config] specifies a value of the wrong type.
* @throws ConfigException.BadValue if the [Config] specifies a value of the correct type, but this in unacceptable according to application-level validation rules.. * @throws ConfigException.BadValue if the [Config] specifies a value of the correct type, but this in unacceptable according to
* application-level validation rules.
*/ */
@Throws(ConfigException.Missing::class, ConfigException.WrongType::class, ConfigException.BadValue::class) @Throws(ConfigException.Missing::class, ConfigException.WrongType::class, ConfigException.BadValue::class)
fun valueIn(configuration: Config): TYPE fun valueIn(configuration: Config, options: Options): TYPE
/** /**
* Returns whether the value is specified by the [Config]. * Returns whether the value is specified by the [Config].
@ -50,27 +51,28 @@ object Configuration {
* Returns a value out of a [Config] if all is good, or null if no value is present. Otherwise, it throws an exception. * Returns a value out of a [Config] if all is good, or null if no value is present. Otherwise, it throws an exception.
* *
* @throws ConfigException.WrongType if the [Config] specifies a value of the wrong type. * @throws ConfigException.WrongType if the [Config] specifies a value of the wrong type.
* @throws ConfigException.BadValue if the [Config] specifies a value of the correct type, but this in unacceptable according to application-level validation rules.. * @throws ConfigException.BadValue if the [Config] specifies a value of the correct type, but this in unacceptable according to
* application-level validation rules.
*/ */
@Throws(ConfigException.WrongType::class, ConfigException.BadValue::class) @Throws(ConfigException.WrongType::class, ConfigException.BadValue::class)
fun valueInOrNull(configuration: Config): TYPE? { fun valueInOrNull(configuration: Config, options: Options): TYPE? {
return when { return when {
isSpecifiedBy(configuration) -> valueIn(configuration) isSpecifiedBy(configuration) -> valueIn(configuration, options)
else -> null else -> null
} }
} }
} }
/** /**
* Able to parse a value from a [Config] and [Configuration.Validation.Options], returning a [Valid] result containing either the value itself, or some [Configuration.Validation.Error]s. * Able to parse a value from a [Config] and [Configuration.Options], returning a [Valid] result containing either the value itself, or some [Configuration.Validation.Error]s.
*/ */
interface Parser<VALUE> { interface Parser<VALUE> {
/** /**
* Returns a [Valid] wrapper either around a valid value extracted from the [Config], or around a set of [Configuration.Validation.Error] with details about what went wrong. * Returns a [Valid] wrapper either around a valid value extracted from the [Config], or around a set of [Configuration.Validation.Error] with details about what went wrong.
*/ */
fun parse(configuration: Config, options: Configuration.Validation.Options = Configuration.Validation.Options.defaults): Valid<VALUE> fun parse(configuration: Config, options: Options = Options.defaults): Valid<VALUE>
} }
} }
@ -109,11 +111,6 @@ object Configuration {
*/ */
interface Definition<TYPE> : Configuration.Property.Metadata, Configuration.Validator, Configuration.Value.Extractor<TYPE>, Configuration.Describer, Configuration.Value.Parser<TYPE> { interface Definition<TYPE> : Configuration.Property.Metadata, Configuration.Validator, Configuration.Value.Extractor<TYPE>, Configuration.Describer, Configuration.Value.Parser<TYPE> {
/**
* Validates target [Config] with default [Configuration.Validation.Options].
*/
fun validate(target: Config): Valid<Config> = validate(target, Configuration.Validation.Options.defaults)
override fun isSpecifiedBy(configuration: Config): Boolean = configuration.hasPath(key) override fun isSpecifiedBy(configuration: Config): Boolean = configuration.hasPath(key)
/** /**
@ -181,9 +178,8 @@ object Configuration {
fun <MAPPED> map(mappedTypeName: String, convert: (TYPE) -> MAPPED): Standard<MAPPED> = mapValid(mappedTypeName) { value -> valid(convert.invoke(value)) } fun <MAPPED> map(mappedTypeName: String, convert: (TYPE) -> MAPPED): Standard<MAPPED> = mapValid(mappedTypeName) { value -> valid(convert.invoke(value)) }
} }
override fun parse(configuration: Config, options: Configuration.Validation.Options): Validated<TYPE, Validation.Error> { override fun parse(configuration: Config, options: Configuration.Options): Validated<TYPE, Validation.Error> {
return validate(configuration, options).mapValid { config -> valid(valueIn(config, options)) }
return validate(configuration, options).mapValid { config -> valid(valueIn(config)) }
} }
companion object { companion object {
@ -199,7 +195,6 @@ object Configuration {
* Returns a [Configuration.Property.Definition.Standard] with value of type [Int]. * Returns a [Configuration.Property.Definition.Standard] with value of type [Int].
*/ */
fun int(key: String, sensitive: Boolean = false): Standard<Int> = long(key, sensitive).mapValid { value -> fun int(key: String, sensitive: Boolean = false): Standard<Int> = long(key, sensitive).mapValid { value ->
try { try {
valid(Math.toIntExact(value)) valid(Math.toIntExact(value))
} catch (e: ArithmeticException) { } catch (e: ArithmeticException) {
@ -210,18 +205,17 @@ object Configuration {
/** /**
* Returns a [Configuration.Property.Definition.Standard] with value of type [Boolean]. * Returns a [Configuration.Property.Definition.Standard] with value of type [Boolean].
*/ */
fun boolean(key: String, sensitive: Boolean = false): Standard<Boolean> = StandardProperty(key, Boolean::class.javaObjectType.simpleName, Config::getBoolean, Config::getBooleanList, sensitive) fun boolean(key: String, sensitive: Boolean = false): Standard<Boolean> = StandardProperty(key, Boolean::class.javaObjectType.simpleName, { config, path, _ -> config.getBoolean(path) }, { config, path, _ -> config.getBooleanList(path) }, sensitive)
/** /**
* Returns a [Configuration.Property.Definition.Standard] with value of type [Double]. * Returns a [Configuration.Property.Definition.Standard] with value of type [Double].
*/ */
fun double(key: String, sensitive: Boolean = false): Standard<Double> = StandardProperty(key, Double::class.javaObjectType.simpleName, Config::getDouble, Config::getDoubleList, sensitive) fun double(key: String, sensitive: Boolean = false): Standard<Double> = StandardProperty(key, Double::class.javaObjectType.simpleName, { config, path, _ -> config.getDouble(path) }, { config, path, _ -> config.getDoubleList(path) }, sensitive)
/** /**
* Returns a [Configuration.Property.Definition.Standard] with value of type [Float]. * Returns a [Configuration.Property.Definition.Standard] with value of type [Float].
*/ */
fun float(key: String, sensitive: Boolean = false): Standard<Float> = double(key, sensitive).mapValid { value -> fun float(key: String, sensitive: Boolean = false): Standard<Float> = double(key, sensitive).mapValid { value ->
val floatValue = value.toFloat() val floatValue = value.toFloat()
if (floatValue.isInfinite() || floatValue.isNaN()) { if (floatValue.isInfinite() || floatValue.isNaN()) {
invalid<Float, Configuration.Validation.Error>(Configuration.Validation.Error.BadValue.of(key, Float::class.javaObjectType.simpleName, "Provided value exceeds Float range.")) invalid<Float, Configuration.Validation.Error>(Configuration.Validation.Error.BadValue.of(key, Float::class.javaObjectType.simpleName, "Provided value exceeds Float range."))
@ -233,24 +227,43 @@ object Configuration {
/** /**
* Returns a [Configuration.Property.Definition.Standard] with value of type [String]. * Returns a [Configuration.Property.Definition.Standard] with value of type [String].
*/ */
fun string(key: String, sensitive: Boolean = false): Standard<String> = StandardProperty(key, String::class.java.simpleName, Config::getString, Config::getStringList, sensitive) fun string(key: String, sensitive: Boolean = false): Standard<String> = StandardProperty(
key,
String::class.java.simpleName,
{ config, path, _ -> config.getString(path) },
{ config, path, _ -> config.getStringList(path) },
sensitive
)
/** /**
* Returns a [Configuration.Property.Definition.Standard] with value of type [Duration]. * Returns a [Configuration.Property.Definition.Standard] with value of type [Duration].
*/ */
fun duration(key: String, sensitive: Boolean = false): Standard<Duration> = StandardProperty(key, Duration::class.java.simpleName, Config::getDuration, Config::getDurationList, sensitive) fun duration(key: String, sensitive: Boolean = false): Standard<Duration> = StandardProperty(key, Duration::class.java.simpleName, { config, path, _ -> config.getDuration(path) }, { config, path, _ -> config.getDurationList(path) }, sensitive)
/** /**
* Returns a [Configuration.Property.Definition.Standard] with value of type [ConfigObject]. * Returns a [Configuration.Property.Definition.Standard] with value of type [ConfigObject].
* It supports an optional [Configuration.Schema], which is used for validation and more when provided. * It supports an optional [Configuration.Schema], which is used for validation and more when provided.
*/ */
fun nestedObject(key: String, schema: Schema? = null, sensitive: Boolean = false): Standard<ConfigObject> = StandardProperty(key, ConfigObject::class.java.simpleName, Config::getObject, Config::getObjectList, sensitive, schema) fun nestedObject(key: String, schema: Schema? = null, sensitive: Boolean = false): Standard<ConfigObject> = StandardProperty(
key,
ConfigObject::class.java.simpleName,
{ config, path, _ -> config.getObject(path) },
{ config, path, _ -> config.getObjectList(path) },
sensitive,
schema
)
/** /**
* Returns a [Configuration.Property.Definition.Standard] with value of type [ENUM]. * Returns a [Configuration.Property.Definition.Standard] with value of type [ENUM].
* This property expects a value in the configuration matching one of the cases of [ENUM], as text, in uppercase. * This property expects a value in the configuration matching one of the cases of [ENUM], as text, in uppercase.
*/ */
fun <ENUM : Enum<ENUM>> enum(key: String, enumClass: KClass<ENUM>, sensitive: Boolean = false): Standard<ENUM> = StandardProperty(key, enumClass.java.simpleName, { conf: Config, propertyKey: String -> conf.getEnum(enumClass.java, propertyKey) }, { conf: Config, propertyKey: String -> conf.getEnumList(enumClass.java, propertyKey) }, sensitive) fun <ENUM : Enum<ENUM>> enum(key: String, enumClass: KClass<ENUM>, sensitive: Boolean = false): Standard<ENUM> = StandardProperty(
key,
enumClass.java.simpleName,
{ conf: Config, propertyKey: String, _ -> conf.getEnum(enumClass.java, propertyKey) },
{ conf: Config, propertyKey: String, _ -> conf.getEnumList(enumClass.java, propertyKey) },
sensitive
)
} }
} }
} }
@ -275,12 +288,7 @@ object Configuration {
*/ */
val properties: Set<Property.Definition<*>> val properties: Set<Property.Definition<*>>
/** override fun describe(configuration: Config, serialiseValue: (Any?) -> ConfigValue, options: Configuration.Options): ConfigValue
* Validates target [Config] with default [Configuration.Validation.Options].
*/
fun validate(target: Config): Valid<Config> = validate(target, Configuration.Validation.Options.defaults)
override fun describe(configuration: Config, serialiseValue: (Any?) -> ConfigValue): ConfigValue
companion object { companion object {
@ -368,35 +376,35 @@ object Configuration {
override fun description() = schema.description() override fun description() = schema.description()
override fun validate(target: Config, options: Validation.Options) = schema.validate(target, options) override fun validate(target: Config, options: Options) = schema.validate(target, options)
override fun describe(configuration: Config, serialiseValue: (Any?) -> ConfigValue) = schema.describe(configuration, serialiseValue) override fun describe(configuration: Config, serialiseValue: (Any?) -> ConfigValue, options: Configuration.Options) = schema.describe(configuration, serialiseValue, options)
final override fun parse(configuration: Config, options: Configuration.Validation.Options): Valid<VALUE> = validate(configuration, options).mapValid(::parseValid) final override fun parse(configuration: Config, options: Options): Valid<VALUE> = validate(configuration, options).mapValid { parseValid(it, options) }
/** /**
* Implement to define further mapping and validation logic, assuming the underlying raw [Config] is correct in terms of this [Configuration.Specification]. * Implement to define further mapping and validation logic, assuming the underlying raw [Config] is correct in terms of this [Configuration.Specification].
*/ */
protected abstract fun parseValid(configuration: Config): Valid<VALUE> protected abstract fun parseValid(configuration: Config, options: Options): Valid<VALUE>
} }
object Validation {
/** /**
* [Config] validation options. * Validation and processing options.
* @property strict whether to raise unknown property keys as errors. * @property strict whether to raise unknown property keys as errors.
*/ */
data class Options(val strict: Boolean) { class Options(val strict: Boolean = false) {
companion object { companion object {
/** /**
* Default [Config] validation options, without [strict] parsing enabled. * Default [Config] options, without [strict] parsing enabled.
*/ */
val defaults: Configuration.Validation.Options = Options(strict = false) val defaults: Configuration.Options = Options()
} }
} }
object Validation {
/** /**
* Super-type for the errors raised by the parsing and validation of a [Config] object. * Super-type for the errors raised by the parsing and validation of a [Config] object.
* *
@ -531,7 +539,7 @@ object Configuration {
} }
/** /**
* Raised when a key-value pair appeared in the [Config] object without a matching property in the [Configuration.Schema], and [Configuration.Validation.Options.strict] was enabled. * Raised when a key-value pair appeared in the [Config] object without a matching property in the [Configuration.Schema], and [Configuration.Options.strict] was enabled.
*/ */
class Unknown private constructor(override val keyName: String, containingPath: List<String> = emptyList()) : Configuration.Validation.Error(keyName, null, message(keyName), containingPath) { class Unknown private constructor(override val keyName: String, containingPath: List<String> = emptyList()) : Configuration.Validation.Error(keyName, null, message(keyName), containingPath) {
@ -586,5 +594,5 @@ object Configuration {
/** /**
* Defines the ability to validate a [Config] object, producing a valid [Config] or a set of [Configuration.Validation.Error]. * Defines the ability to validate a [Config] object, producing a valid [Config] or a set of [Configuration.Validation.Error].
*/ */
interface Validator : net.corda.common.validation.internal.Validator<Config, Configuration.Validation.Error, Configuration.Validation.Options> interface Validator : net.corda.common.validation.internal.Validator<Config, Configuration.Validation.Error, Configuration.Options>
} }

View File

@ -5,10 +5,9 @@ import net.corda.common.validation.internal.Validated
import net.corda.common.validation.internal.Validated.Companion.invalid import net.corda.common.validation.internal.Validated.Companion.invalid
import net.corda.common.validation.internal.Validated.Companion.valid import net.corda.common.validation.internal.Validated.Companion.valid
internal class LongProperty(key: String, sensitive: Boolean = false) : StandardProperty<Long>(key, Long::class.javaObjectType.simpleName, Config::getLong, Config::getLongList, sensitive) { internal class LongProperty(key: String, sensitive: Boolean = false) : StandardProperty<Long>(key, Long::class.javaObjectType.simpleName, { config, path, _ -> config.getLong(path) }, { config, path, _ -> config.getLongList(path) }, sensitive) {
override fun validate(target: Config, options: Configuration.Validation.Options): Valid<Config> {
override fun validate(target: Config, options: Configuration.Options): Valid<Config> {
val validated = super.validate(target, options) val validated = super.validate(target, options)
if (validated.isValid && target.getValue(key).unwrapped().toString().contains(".")) { if (validated.isValid && target.getValue(key).unwrapped().toString().contains(".")) {
return invalid(ConfigException.WrongType(target.origin(), key, Long::class.javaObjectType.simpleName, Double::class.javaObjectType.simpleName).toValidationError(key, typeName)) return invalid(ConfigException.WrongType(target.origin(), key, Long::class.javaObjectType.simpleName, Double::class.javaObjectType.simpleName).toValidationError(key, typeName))
@ -17,9 +16,11 @@ internal class LongProperty(key: String, sensitive: Boolean = false) : StandardP
} }
} }
internal open class StandardProperty<TYPE : Any>(override val key: String, typeNameArg: String, private val extractSingleValue: (Config, String) -> TYPE, internal val extractListValue: (Config, String) -> List<TYPE>, override val isSensitive: Boolean = false, final override val schema: Configuration.Schema? = null) : Configuration.Property.Definition.Standard<TYPE> { typealias ValueSelector<T> = (Config, String, Configuration.Options) -> T
override fun valueIn(configuration: Config) = extractSingleValue.invoke(configuration, key) internal open class StandardProperty<TYPE : Any>(override val key: String, typeNameArg: String, private val extractSingleValue: ValueSelector<TYPE>, internal val extractListValue: ValueSelector<List<TYPE>>, override val isSensitive: Boolean = false, final override val schema: Configuration.Schema? = null) : Configuration.Property.Definition.Standard<TYPE> {
override fun valueIn(configuration: Config, options: Configuration.Options) = extractSingleValue.invoke(configuration, key, options)
override val typeName: String = schema?.let { "#${it.name ?: "Object@$key"}" } ?: typeNameArg override val typeName: String = schema?.let { "#${it.name ?: "Object@$key"}" } ?: typeNameArg
@ -29,20 +30,18 @@ internal open class StandardProperty<TYPE : Any>(override val key: String, typeN
override fun list(): Configuration.Property.Definition.RequiredList<TYPE> = ListProperty(this) override fun list(): Configuration.Property.Definition.RequiredList<TYPE> = ListProperty(this)
override fun describe(configuration: Config, serialiseValue: (Any?) -> ConfigValue): ConfigValue { override fun describe(configuration: Config, serialiseValue: (Any?) -> ConfigValue, options: Configuration.Options): ConfigValue {
if (isSensitive) { if (isSensitive) {
return valueDescription(Configuration.Property.Definition.SENSITIVE_DATA_PLACEHOLDER, serialiseValue) return valueDescription(Configuration.Property.Definition.SENSITIVE_DATA_PLACEHOLDER, serialiseValue)
} }
return schema?.describe(configuration.getConfig(key), serialiseValue) ?: valueDescription(valueIn(configuration), serialiseValue) return schema?.describe(configuration.getConfig(key), serialiseValue, options) ?: valueDescription(valueIn(configuration, options), serialiseValue)
} }
override val isMandatory = true override val isMandatory = true
override fun validate(target: Config, options: Configuration.Validation.Options): Valid<Config> { override fun validate(target: Config, options: Configuration.Options): Valid<Config> {
val errors = mutableSetOf<Configuration.Validation.Error>() val errors = mutableSetOf<Configuration.Validation.Error>()
errors += errorsWhenExtractingValue(target) errors += errorsWhenExtractingValue(target, options)
if (errors.isEmpty()) { if (errors.isEmpty()) {
schema?.let { nestedSchema -> schema?.let { nestedSchema ->
val nestedConfig: Config? = target.getConfig(key) val nestedConfig: Config? = target.getConfig(key)
@ -61,15 +60,19 @@ private class ListProperty<TYPE : Any>(delegate: StandardProperty<TYPE>) : Requi
override val typeName: String = "List<${delegate.typeName}>" override val typeName: String = "List<${delegate.typeName}>"
override fun valueIn(configuration: Config): List<TYPE> = delegate.extractListValue.invoke(configuration, key) override fun valueIn(configuration: Config, options: Configuration.Options): List<TYPE> = delegate.extractListValue.invoke(configuration, key, options)
override fun validate(target: Config, options: Configuration.Validation.Options): Valid<Config> {
override fun validate(target: Config, options: Configuration.Options): Valid<Config> {
val errors = mutableSetOf<Configuration.Validation.Error>() val errors = mutableSetOf<Configuration.Validation.Error>()
errors += errorsWhenExtractingValue(target) errors += errorsWhenExtractingValue(target, options)
if (errors.isEmpty()) { if (errors.isEmpty()) {
delegate.schema?.let { schema -> delegate.schema?.let { schema ->
errors += valueIn(target).asSequence().map { element -> element as ConfigObject }.map(ConfigObject::toConfig).mapIndexed { index, targetConfig -> schema.validate(targetConfig, options).errors.map { error -> error.withContainingPath(*error.containingPath(index).toTypedArray()) } }.fold(emptyList<Configuration.Validation.Error>()) { one, other -> one + other }.toSet() errors += valueIn(target, options).asSequence()
.map { element -> element as ConfigObject }
.map(ConfigObject::toConfig)
.mapIndexed { index, targetConfig -> schema.validate(targetConfig, options).errors.map { error -> error.withContainingPath(*error.containingPath(index).toTypedArray()) } }
.fold(emptyList<Configuration.Validation.Error>()) { one, other -> one + other }
.toSet()
} }
} }
return Validated.withResult(target, errors) return Validated.withResult(target, errors)
@ -77,17 +80,16 @@ private class ListProperty<TYPE : Any>(delegate: StandardProperty<TYPE>) : Requi
override fun <MAPPED> mapValid(mappedTypeName: String, convert: (List<TYPE>) -> Validated<MAPPED, Configuration.Validation.Error>): Configuration.Property.Definition.Required<MAPPED> = ListMappingProperty(this, mappedTypeName, convert) override fun <MAPPED> mapValid(mappedTypeName: String, convert: (List<TYPE>) -> Validated<MAPPED, Configuration.Validation.Error>): Configuration.Property.Definition.Required<MAPPED> = ListMappingProperty(this, mappedTypeName, convert)
override fun describe(configuration: Config, serialiseValue: (Any?) -> ConfigValue): ConfigValue { override fun describe(configuration: Config, serialiseValue: (Any?) -> ConfigValue, options: Configuration.Options): ConfigValue {
if (isSensitive) { if (isSensitive) {
return valueDescription(Configuration.Property.Definition.SENSITIVE_DATA_PLACEHOLDER, serialiseValue) return valueDescription(Configuration.Property.Definition.SENSITIVE_DATA_PLACEHOLDER, serialiseValue)
} }
return when { return when {
delegate.schema != null -> { delegate.schema != null -> {
val elementsDescription = valueIn(configuration).asSequence().map { it as ConfigObject }.map(ConfigObject::toConfig).map { delegate.schema.describe(it, serialiseValue) }.toList() val elementsDescription = valueIn(configuration, options).asSequence().map { it as ConfigObject }.map(ConfigObject::toConfig).map { delegate.schema.describe(it, serialiseValue, options) }.toList()
ConfigValueFactory.fromIterable(elementsDescription) ConfigValueFactory.fromIterable(elementsDescription)
} }
else -> valueDescription(valueIn(configuration), serialiseValue) else -> valueDescription(valueIn(configuration, options), serialiseValue)
} }
} }
@ -106,16 +108,17 @@ private class OptionalPropertyWithDefault<TYPE>(delegate: Configuration.Property
override val typeName: String = delegate.typeName.removeSuffix("?") override val typeName: String = delegate.typeName.removeSuffix("?")
override fun describe(configuration: Config, serialiseValue: (Any?) -> ConfigValue): ConfigValue? = delegate.describe(configuration, serialiseValue) ?: valueDescription(if (isSensitive) Configuration.Property.Definition.SENSITIVE_DATA_PLACEHOLDER else defaultValue, serialiseValue) override fun describe(configuration: Config, serialiseValue: (Any?) -> ConfigValue, options: Configuration.Options): ConfigValue? = delegate.describe(configuration, serialiseValue, options) ?: valueDescription(if (isSensitive) Configuration.Property.Definition.SENSITIVE_DATA_PLACEHOLDER else defaultValue, serialiseValue)
override fun valueIn(configuration: Config): TYPE = delegate.valueIn(configuration) ?: defaultValue override fun valueIn(configuration: Config, options: Configuration.Options): TYPE = delegate.valueIn(configuration, options) ?: defaultValue
override fun validate(target: Config, options: Configuration.Validation.Options): Valid<Config> = delegate.validate(target, options) override fun validate(target: Config, options: Configuration.Options): Valid<Config> = delegate.validate(target, options)
} }
private class FunctionalProperty<TYPE, MAPPED>(delegate: Configuration.Property.Definition.Standard<TYPE>, private val mappedTypeName: String, internal val extractListValue: (Config, String) -> List<TYPE>, private val convert: (TYPE) -> Valid<MAPPED>) : RequiredDelegatedProperty<MAPPED, Configuration.Property.Definition.Standard<TYPE>>(delegate), Configuration.Property.Definition.Standard<MAPPED> { private class FunctionalProperty<TYPE, MAPPED>(delegate: Configuration.Property.Definition.Standard<TYPE>, private val mappedTypeName: String, internal val extractListValue: ValueSelector<List<TYPE>>, private val convert: (TYPE) -> Valid<MAPPED>)
: RequiredDelegatedProperty<MAPPED, Configuration.Property.Definition.Standard<TYPE>>(delegate), Configuration.Property.Definition.Standard<MAPPED> {
override fun valueIn(configuration: Config) = convert.invoke(delegate.valueIn(configuration)).value() override fun valueIn(configuration: Config, options: Configuration.Options) = convert.invoke(delegate.valueIn(configuration, options)).value()
override val typeName: String = if (super.typeName == "#$mappedTypeName") super.typeName else "$mappedTypeName(${super.typeName})" override val typeName: String = if (super.typeName == "#$mappedTypeName") super.typeName else "$mappedTypeName(${super.typeName})"
@ -123,29 +126,31 @@ private class FunctionalProperty<TYPE, MAPPED>(delegate: Configuration.Property.
override fun list(): Configuration.Property.Definition.RequiredList<MAPPED> = FunctionalListProperty(this) override fun list(): Configuration.Property.Definition.RequiredList<MAPPED> = FunctionalListProperty(this)
override fun validate(target: Config, options: Configuration.Validation.Options): Valid<Config> { override fun validate(target: Config, options: Configuration.Options): Valid<Config> {
val errors = mutableSetOf<Configuration.Validation.Error>() val errors = mutableSetOf<Configuration.Validation.Error>()
errors += delegate.validate(target, options).errors errors += delegate.validate(target, options).errors
if (errors.isEmpty()) { if (errors.isEmpty()) {
errors += convert.invoke(delegate.valueIn(target)).mapErrors { error -> error.with(delegate.key, mappedTypeName) }.errors errors += convert.invoke(delegate.valueIn(target, options)).mapErrors { error -> error.with(delegate.key, mappedTypeName) }.errors
} }
return Validated.withResult(target, errors) return Validated.withResult(target, errors)
} }
override fun describe(configuration: Config, serialiseValue: (Any?) -> ConfigValue) = delegate.describe(configuration, serialiseValue) override fun describe(configuration: Config, serialiseValue: (Any?) -> ConfigValue, options: Configuration.Options) = delegate.describe(configuration, serialiseValue, options)
} }
private class FunctionalListProperty<RAW, TYPE>(delegate: FunctionalProperty<RAW, TYPE>) : RequiredDelegatedProperty<List<TYPE>, FunctionalProperty<RAW, TYPE>>(delegate), Configuration.Property.Definition.RequiredList<TYPE> { private class FunctionalListProperty<RAW, TYPE>(delegate: FunctionalProperty<RAW, TYPE>) : RequiredDelegatedProperty<List<TYPE>, FunctionalProperty<RAW, TYPE>>(delegate), Configuration.Property.Definition.RequiredList<TYPE> {
override val typeName: String = "List<${super.typeName}>" override val typeName: String = "List<${super.typeName}>"
override fun valueIn(configuration: Config): List<TYPE> = delegate.extractListValue.invoke(configuration, key).asSequence().map { configObject(key to ConfigValueFactory.fromAnyRef(it)) }.map(ConfigObject::toConfig).map(delegate::valueIn).toList() override fun valueIn(configuration: Config, options: Configuration.Options): List<TYPE> = delegate.extractListValue.invoke(configuration, key, options).asSequence()
.map { configObject(key to ConfigValueFactory.fromAnyRef(it)) }
override fun validate(target: Config, options: Configuration.Validation.Options): Valid<Config> { .map(ConfigObject::toConfig)
.map { delegate.valueIn(it, options) }
.toList()
override fun validate(target: Config, options: Configuration.Options): Valid<Config> {
val list = try { val list = try {
delegate.extractListValue.invoke(target, key) delegate.extractListValue.invoke(target, key, options)
} catch (e: ConfigException) { } catch (e: ConfigException) {
if (isErrorExpected(e)) { if (isErrorExpected(e)) {
return invalid(e.toValidationError(key, typeName)) return invalid(e.toValidationError(key, typeName))
@ -153,7 +158,11 @@ private class FunctionalListProperty<RAW, TYPE>(delegate: FunctionalProperty<RAW
throw e throw e
} }
} }
val errors = list.asSequence().map { configObject(key to ConfigValueFactory.fromAnyRef(it)) }.mapIndexed { index, value -> delegate.validate(value.toConfig(), options).errors.map { error -> error.withContainingPath(*error.containingPath(index).toTypedArray()) } }.fold(emptyList<Configuration.Validation.Error>()) { one, other -> one + other }.toSet() val errors = list.asSequence()
.map { configObject(key to ConfigValueFactory.fromAnyRef(it)) }
.mapIndexed { index, value -> delegate.validate(value.toConfig(), options).errors.map { error -> error.withContainingPath(*error.containingPath(index).toTypedArray()) } }
.fold(emptyList<Configuration.Validation.Error>()) { one, other -> one + other }
.toSet()
return Validated.withResult(target, errors) return Validated.withResult(target, errors)
} }
@ -165,12 +174,11 @@ private class FunctionalListProperty<RAW, TYPE>(delegate: FunctionalProperty<RAW
} }
} }
override fun describe(configuration: Config, serialiseValue: (Any?) -> ConfigValue): ConfigValue { override fun describe(configuration: Config, serialiseValue: (Any?) -> ConfigValue, options: Configuration.Options): ConfigValue {
if (isSensitive) { if (isSensitive) {
return valueDescription(Configuration.Property.Definition.SENSITIVE_DATA_PLACEHOLDER, serialiseValue) return valueDescription(Configuration.Property.Definition.SENSITIVE_DATA_PLACEHOLDER, serialiseValue)
} }
return delegate.schema?.let { schema -> valueDescription(valueIn(configuration).asSequence().map { element -> valueDescription(element, serialiseValue) }.map { it as ConfigObject }.map(ConfigObject::toConfig).map { schema.describe(it, serialiseValue) }.toList(), serialiseValue) } ?: valueDescription(valueIn(configuration), serialiseValue) return delegate.schema?.let { schema -> valueDescription(valueIn(configuration, options).asSequence() .map { element -> valueDescription(element, serialiseValue) } .map { it as ConfigObject } .map(ConfigObject::toConfig) .map { schema.describe(it, serialiseValue, options) } .toList(), serialiseValue) } ?: valueDescription(valueIn(configuration, options), serialiseValue)
} }
override fun <MAPPED> mapValid(mappedTypeName: String, convert: (List<TYPE>) -> Validated<MAPPED, Configuration.Validation.Error>): Configuration.Property.Definition.Required<MAPPED> = ListMappingProperty(this, mappedTypeName, convert) override fun <MAPPED> mapValid(mappedTypeName: String, convert: (List<TYPE>) -> Validated<MAPPED, Configuration.Validation.Error>): Configuration.Property.Definition.Required<MAPPED> = ListMappingProperty(this, mappedTypeName, convert)
@ -187,18 +195,16 @@ private class OptionalDelegatedProperty<TYPE>(private val delegate: Configuratio
override val typeName: String = "${delegate.typeName}?" override val typeName: String = "${delegate.typeName}?"
override fun describe(configuration: Config, serialiseValue: (Any?) -> ConfigValue) = if (isSpecifiedBy(configuration)) delegate.describe(configuration, serialiseValue) else null override fun describe(configuration: Config, serialiseValue: (Any?) -> ConfigValue, options: Configuration.Options) = if (isSpecifiedBy(configuration)) delegate.describe(configuration, serialiseValue, options) else null
override fun valueIn(configuration: Config): TYPE? {
override fun valueIn(configuration: Config, options: Configuration.Options): TYPE? {
return when { return when {
isSpecifiedBy(configuration) -> delegate.valueIn(configuration) isSpecifiedBy(configuration) -> delegate.valueIn(configuration, options)
else -> null else -> null
} }
} }
override fun validate(target: Config, options: Configuration.Validation.Options): Valid<Config> { override fun validate(target: Config, options: Configuration.Options): Valid<Config> {
val result = delegate.validate(target, options) val result = delegate.validate(target, options)
val errors = result.errors val errors = result.errors
val missingValueError = errors.asSequence().filterIsInstance<Configuration.Validation.Error.MissingValue>().filter { it.pathAsString == key }.singleOrNull() val missingValueError = errors.asSequence().filterIsInstance<Configuration.Validation.Error.MissingValue>().filter { it.pathAsString == key }.singleOrNull()
@ -221,18 +227,17 @@ private abstract class RequiredDelegatedProperty<TYPE, DELEGATE : Configuration.
private class ListMappingProperty<TYPE, MAPPED>(private val delegate: Configuration.Property.Definition.RequiredList<TYPE>, private val mappedTypeName: String, private val convert: (List<TYPE>) -> Validated<MAPPED, Configuration.Validation.Error>) : Configuration.Property.Definition.Required<MAPPED> { private class ListMappingProperty<TYPE, MAPPED>(private val delegate: Configuration.Property.Definition.RequiredList<TYPE>, private val mappedTypeName: String, private val convert: (List<TYPE>) -> Validated<MAPPED, Configuration.Validation.Error>) : Configuration.Property.Definition.Required<MAPPED> {
override fun describe(configuration: Config, serialiseValue: (Any?) -> ConfigValue): ConfigValue? = delegate.describe(configuration, serialiseValue) override fun describe(configuration: Config, serialiseValue: (Any?) -> ConfigValue, options: Configuration.Options): ConfigValue? = delegate.describe(configuration, serialiseValue, options)
override fun valueIn(configuration: Config) = convert.invoke(delegate.valueIn(configuration)).value() override fun valueIn(configuration: Config, options: Configuration.Options) = convert.invoke(delegate.valueIn(configuration, options)).value()
override fun optional(): Configuration.Property.Definition.Optional<MAPPED> = OptionalDelegatedProperty(this) override fun optional(): Configuration.Property.Definition.Optional<MAPPED> = OptionalDelegatedProperty(this)
override fun validate(target: Config, options: Configuration.Validation.Options): Validated<Config, Configuration.Validation.Error> { override fun validate(target: Config, options: Configuration.Options): Validated<Config, Configuration.Validation.Error> {
val errors = mutableSetOf<Configuration.Validation.Error>() val errors = mutableSetOf<Configuration.Validation.Error>()
errors += delegate.validate(target, options).errors errors += delegate.validate(target, options).errors
if (errors.isEmpty()) { if (errors.isEmpty()) {
errors += convert.invoke(delegate.valueIn(target)).mapErrors { error -> error.with(delegate.key, mappedTypeName) }.errors errors += convert.invoke(delegate.valueIn(target, options)).mapErrors { error -> error.with(delegate.key, mappedTypeName) }.errors
} }
return Validated.withResult(target, errors) return Validated.withResult(target, errors)
} }
@ -248,7 +253,6 @@ private class ListMappingProperty<TYPE, MAPPED>(private val delegate: Configurat
} }
fun ConfigException.toValidationError(keyName: String? = null, typeName: String): Configuration.Validation.Error { fun ConfigException.toValidationError(keyName: String? = null, typeName: String): Configuration.Validation.Error {
val toError = when (this) { val toError = when (this) {
is ConfigException.Missing -> Configuration.Validation.Error.MissingValue.Companion::of is ConfigException.Missing -> Configuration.Validation.Error.MissingValue.Companion::of
is ConfigException.WrongType -> Configuration.Validation.Error.WrongType.Companion::of is ConfigException.WrongType -> Configuration.Validation.Error.WrongType.Companion::of
@ -260,10 +264,9 @@ fun ConfigException.toValidationError(keyName: String? = null, typeName: String)
return toError.invoke(message!!, keyName, typeName, emptyList()) return toError.invoke(message!!, keyName, typeName, emptyList())
} }
private fun Configuration.Property.Definition<*>.errorsWhenExtractingValue(target: Config): Set<Configuration.Validation.Error> { private fun Configuration.Property.Definition<*>.errorsWhenExtractingValue(target: Config, options: Configuration.Options): Set<Configuration.Validation.Error> {
try { try {
valueIn(target) valueIn(target, options)
return emptySet() return emptySet()
} catch (exception: ConfigException) { } catch (exception: ConfigException) {
if (isErrorExpected(exception)) { if (isErrorExpected(exception)) {

View File

@ -16,7 +16,7 @@ internal class Schema(override val name: String?, unorderedProperties: Iterable<
} }
} }
override fun validate(target: Config, options: Configuration.Validation.Options): Valid<Config> { override fun validate(target: Config, options: Configuration.Options): Valid<Config> {
val propertyErrors = properties.flatMap { property -> val propertyErrors = properties.flatMap { property ->
property.validate(target, options).errors property.validate(target, options).errors
@ -47,9 +47,9 @@ internal class Schema(override val name: String?, unorderedProperties: Iterable<
return description.toString() return description.toString()
} }
override fun describe(configuration: Config, serialiseValue: (Any?) -> ConfigValue): ConfigValue { override fun describe(configuration: Config, serialiseValue: (Any?) -> ConfigValue, options: Configuration.Options): ConfigValue {
return properties.asSequence().map { it.key to it.describe(configuration, serialiseValue) }.filter { it.second != null }.fold(configObject()) { config, (key, value) -> config.withValue(key, value) } return properties.asSequence().map { it.key to it.describe(configuration, serialiseValue, options) }.filter { it.second != null }.fold(configObject()) { config, (key, value) -> config.withValue(key, value) }
} }
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {

View File

@ -21,7 +21,11 @@ inline fun <TYPE, reified MAPPED> Configuration.Property.Definition.RequiredList
inline fun <TYPE, reified MAPPED> Configuration.Property.Definition.RequiredList<TYPE>.map(noinline convert: (List<TYPE>) -> MAPPED): Configuration.Property.Definition.Required<MAPPED> = map(MAPPED::class.java.simpleName, convert) inline fun <TYPE, reified MAPPED> Configuration.Property.Definition.RequiredList<TYPE>.map(noinline convert: (List<TYPE>) -> MAPPED): Configuration.Property.Definition.Required<MAPPED> = map(MAPPED::class.java.simpleName, convert)
operator fun <TYPE> Config.get(property: Configuration.Property.Definition<TYPE>): TYPE = property.valueIn(this) fun Config.withOptions(options: Configuration.Options) = ConfigurationWithOptions(this, options)
data class ConfigurationWithOptions(private val config: Config, private val options: Configuration.Options) {
operator fun <TYPE> get(property: Configuration.Value.Extractor<TYPE>): TYPE = property.valueIn(config, options)
}
inline fun <reified NESTED : Any> Configuration.Specification<*>.nested(specification: Configuration.Specification<NESTED>, key: String? = null, sensitive: Boolean = false): PropertyDelegate.Standard<NESTED> = nestedObject(schema = specification, key = key, sensitive = sensitive).map(ConfigObject::toConfig).mapValid { value -> specification.parse(value) } inline fun <reified NESTED : Any> Configuration.Specification<*>.nested(specification: Configuration.Specification<NESTED>, key: String? = null, sensitive: Boolean = false): PropertyDelegate.Standard<NESTED> = nestedObject(schema = specification, key = key, sensitive = sensitive).map(ConfigObject::toConfig).mapValid { value -> specification.parse(value) }
@ -66,15 +70,6 @@ internal typealias Valid<TARGET> = Validated<TARGET, Configuration.Validation.Er
internal fun <TYPE> valid(target: TYPE) = Validated.valid<TYPE, Configuration.Validation.Error>(target) internal fun <TYPE> valid(target: TYPE) = Validated.valid<TYPE, Configuration.Validation.Error>(target)
/**
* Value extracted from a configuration file is a function of the actual value specified and configuration options.
* E.g. password value may be stored in the encrypted form rather than in a clear text.
*/
data class ConfigurationWithOptions(private val config: Config, private val options: Configuration.Validation.Options) {
operator fun <TYPE> get(property: Configuration.Property.Definition<TYPE>): TYPE = property.valueIn(config)
operator fun <TYPE> get(property: Configuration.Value.Extractor<TYPE>): TYPE = property.valueIn(config)
}
/** /**
* Helper interface to mark objects that will have [ConfigurationWithOptions] in them. * Helper interface to mark objects that will have [ConfigurationWithOptions] in them.
*/ */

View File

@ -6,21 +6,17 @@ import net.corda.common.configuration.parsing.internal.Valid
import net.corda.common.configuration.parsing.internal.valid import net.corda.common.configuration.parsing.internal.valid
internal class VersionExtractor(versionPath: String, versionDefaultValue: Int) : Configuration.Version.Extractor { internal class VersionExtractor(versionPath: String, versionDefaultValue: Int) : Configuration.Version.Extractor {
private val containingPath = versionPath.split(".").let { if (it.size > 1) it.subList(0, it.size - 1) else null } private val containingPath = versionPath.split(".").let { if (it.size > 1) it.subList(0, it.size - 1) else null }
private val key = versionPath.split(".").last() private val key = versionPath.split(".").last()
private val spec = Spec(key, versionDefaultValue, containingPath?.joinToString(".")) private val spec = Spec(key, versionDefaultValue, containingPath?.joinToString("."))
override fun parse(configuration: Config, options: Configuration.Validation.Options): Valid<Int> { override fun parse(configuration: Config, options: Configuration.Options): Valid<Int> {
return spec.parse(configuration) return spec.parse(configuration)
} }
private class Spec(key: String, versionDefaultValue: Int, prefix: String?) : Configuration.Specification<Int>("Version", prefix) { private class Spec(key: String, versionDefaultValue: Int, prefix: String?) : Configuration.Specification<Int>("Version", prefix) {
private val version by int(key = key).optional().withDefaultValue(versionDefaultValue) private val version by int(key = key).optional().withDefaultValue(versionDefaultValue)
override fun parseValid(configuration: Config, options: Configuration.Options) = valid(version.valueIn(configuration, options))
override fun parseValid(configuration: Config) = valid(version.valueIn(configuration))
} }
} }

View File

@ -21,8 +21,8 @@ class PropertyTest {
assertThat(property.key).isEqualTo(key) assertThat(property.key).isEqualTo(key)
assertThat(property.isMandatory).isTrue() assertThat(property.isMandatory).isTrue()
assertThat(property.isSpecifiedBy(configuration)).isTrue() assertThat(property.isSpecifiedBy(configuration)).isTrue()
assertThat(property.valueIn(configuration)).isEqualTo(value) assertThat(property.valueIn(configuration, Configuration.Options.defaults)).isEqualTo(value)
assertThat(configuration[property]).isEqualTo(value) assertThat(configuration.withOptions(Configuration.Options.defaults)[property]).isEqualTo(value)
} }
@Test(timeout=300_000) @Test(timeout=300_000)
@ -38,7 +38,7 @@ class PropertyTest {
assertThat(property.key).isEqualTo(key) assertThat(property.key).isEqualTo(key)
assertThat(property.isMandatory).isTrue() assertThat(property.isMandatory).isTrue()
assertThat(property.isSpecifiedBy(configuration)).isTrue() assertThat(property.isSpecifiedBy(configuration)).isTrue()
assertThatThrownBy { property.valueIn(configuration) }.isInstanceOf(ConfigException.WrongType::class.java) assertThatThrownBy { property.valueIn(configuration, Configuration.Options.defaults) }.isInstanceOf(ConfigException.WrongType::class.java)
} }
@Test(timeout=300_000) @Test(timeout=300_000)
@ -54,7 +54,7 @@ class PropertyTest {
assertThat(property.key).isEqualTo(key) assertThat(property.key).isEqualTo(key)
assertThat(property.isMandatory).isTrue() assertThat(property.isMandatory).isTrue()
assertThat(property.isSpecifiedBy(configuration)).isTrue() assertThat(property.isSpecifiedBy(configuration)).isTrue()
assertThat(property.valueIn(configuration)).isEqualTo(value) assertThat(property.valueIn(configuration, Configuration.Options.defaults)).isEqualTo(value)
} }
@Test(timeout=300_000) @Test(timeout=300_000)
@ -70,7 +70,7 @@ class PropertyTest {
assertThat(property.key).isEqualTo(key) assertThat(property.key).isEqualTo(key)
assertThat(property.isMandatory).isTrue() assertThat(property.isMandatory).isTrue()
assertThat(property.isSpecifiedBy(configuration)).isTrue() assertThat(property.isSpecifiedBy(configuration)).isTrue()
assertThat(property.valueIn(configuration)).isEqualTo(value.max()) assertThat(property.valueIn(configuration, Configuration.Options.defaults)).isEqualTo(value.max())
} }
@Test(timeout=300_000) @Test(timeout=300_000)
@ -85,7 +85,7 @@ class PropertyTest {
assertThat(property.key).isEqualTo(key) assertThat(property.key).isEqualTo(key)
assertThat(property.isMandatory).isFalse() assertThat(property.isMandatory).isFalse()
assertThat(property.isSpecifiedBy(configuration)).isFalse() assertThat(property.isSpecifiedBy(configuration)).isFalse()
assertThat(property.valueIn(configuration)).isEqualTo(null) assertThat(property.valueIn(configuration, Configuration.Options.defaults)).isEqualTo(null)
} }
@Test(timeout=300_000) @Test(timeout=300_000)
@ -101,7 +101,7 @@ class PropertyTest {
assertThat(property.key).isEqualTo(key) assertThat(property.key).isEqualTo(key)
assertThat(property.isMandatory).isTrue() assertThat(property.isMandatory).isTrue()
assertThat(property.isSpecifiedBy(configuration)).isTrue() assertThat(property.isSpecifiedBy(configuration)).isTrue()
assertThat(property.valueIn(configuration)).isEqualTo(value.max()) assertThat(property.valueIn(configuration, Configuration.Options.defaults)).isEqualTo(value.max())
} }
@Test(timeout=300_000) @Test(timeout=300_000)
@ -116,7 +116,7 @@ class PropertyTest {
assertThat(property.key).isEqualTo(key) assertThat(property.key).isEqualTo(key)
assertThat(property.isMandatory).isFalse() assertThat(property.isMandatory).isFalse()
assertThat(property.isSpecifiedBy(configuration)).isFalse() assertThat(property.isSpecifiedBy(configuration)).isFalse()
assertThat(property.valueIn(configuration)).isEqualTo(null) assertThat(property.valueIn(configuration, Configuration.Options.defaults)).isEqualTo(null)
} }
@Test(timeout=300_000) @Test(timeout=300_000)
@ -132,7 +132,7 @@ class PropertyTest {
assertThat(property.key).isEqualTo(key) assertThat(property.key).isEqualTo(key)
assertThat(property.isMandatory).isFalse() assertThat(property.isMandatory).isFalse()
assertThat(property.isSpecifiedBy(configuration)).isTrue() assertThat(property.isSpecifiedBy(configuration)).isTrue()
assertThat(property.valueIn(configuration)).isEqualTo(value) assertThat(property.valueIn(configuration, Configuration.Options.defaults)).isEqualTo(value)
} }
@Test(timeout=300_000) @Test(timeout=300_000)
@ -147,7 +147,7 @@ class PropertyTest {
assertThat(property.key).isEqualTo(key) assertThat(property.key).isEqualTo(key)
assertThat(property.isMandatory).isFalse() assertThat(property.isMandatory).isFalse()
assertThat(property.isSpecifiedBy(configuration)).isFalse() assertThat(property.isSpecifiedBy(configuration)).isFalse()
assertThat(property.valueIn(configuration)).isNull() assertThat(property.valueIn(configuration, Configuration.Options.defaults)).isNull()
} }
@ -164,7 +164,7 @@ class PropertyTest {
assertThat(property.key).isEqualTo(key) assertThat(property.key).isEqualTo(key)
assertThat(property.isMandatory).isFalse() assertThat(property.isMandatory).isFalse()
assertThat(property.isSpecifiedBy(configuration)).isFalse() assertThat(property.isSpecifiedBy(configuration)).isFalse()
assertThat(property.valueIn(configuration)).isEqualTo(defaultValue) assertThat(property.valueIn(configuration, Configuration.Options.defaults)).isEqualTo(defaultValue)
} }
@Test(timeout=300_000) @Test(timeout=300_000)
@ -179,7 +179,7 @@ class PropertyTest {
assertThat(property.key).isEqualTo(key) assertThat(property.key).isEqualTo(key)
assertThat(property.isMandatory).isTrue() assertThat(property.isMandatory).isTrue()
assertThat(property.isSpecifiedBy(configuration)).isFalse() assertThat(property.isSpecifiedBy(configuration)).isFalse()
assertThatThrownBy { property.valueIn(configuration) }.isInstanceOf(ConfigException.Missing::class.java) assertThatThrownBy { property.valueIn(configuration, Configuration.Options.defaults) }.isInstanceOf(ConfigException.Missing::class.java)
} }
@Test(timeout=300_000) @Test(timeout=300_000)
@ -195,7 +195,7 @@ class PropertyTest {
assertThat(property.key).isEqualTo(key) assertThat(property.key).isEqualTo(key)
assertThat(property.isMandatory).isFalse() assertThat(property.isMandatory).isFalse()
assertThat(property.isSpecifiedBy(configuration)).isTrue() assertThat(property.isSpecifiedBy(configuration)).isTrue()
assertThat(property.valueIn(configuration)).isEqualTo(value) assertThat(property.valueIn(configuration, Configuration.Options.defaults)).isEqualTo(value)
} }
@Test(timeout=300_000) @Test(timeout=300_000)
@ -211,7 +211,7 @@ class PropertyTest {
assertThat(property.key).isEqualTo(key) assertThat(property.key).isEqualTo(key)
assertThat(property.isMandatory).isFalse() assertThat(property.isMandatory).isFalse()
assertThat(property.isSpecifiedBy(configuration)).isTrue() assertThat(property.isSpecifiedBy(configuration)).isTrue()
assertThatThrownBy { property.valueIn(configuration) }.isInstanceOf(ConfigException.WrongType::class.java) assertThatThrownBy { property.valueIn(configuration, Configuration.Options.defaults) }.isInstanceOf(ConfigException.WrongType::class.java)
} }
@Test(timeout=300_000) @Test(timeout=300_000)
@ -226,7 +226,7 @@ class PropertyTest {
assertThat(property.key).isEqualTo(key) assertThat(property.key).isEqualTo(key)
assertThat(property.isMandatory).isFalse() assertThat(property.isMandatory).isFalse()
assertThat(property.isSpecifiedBy(configuration)).isFalse() assertThat(property.isSpecifiedBy(configuration)).isFalse()
assertThat(property.valueIn(configuration)).isNull() assertThat(property.valueIn(configuration, Configuration.Options.defaults)).isNull()
} }
@Test(timeout=300_000) @Test(timeout=300_000)
@ -242,6 +242,6 @@ class PropertyTest {
assertThat(property.key).isEqualTo(key) assertThat(property.key).isEqualTo(key)
assertThat(property.isMandatory).isFalse() assertThat(property.isMandatory).isFalse()
assertThat(property.isSpecifiedBy(configuration)).isFalse() assertThat(property.isSpecifiedBy(configuration)).isFalse()
assertThat(property.valueIn(configuration)).isEqualTo(defaultValue) assertThat(property.valueIn(configuration, Configuration.Options.defaults)).isEqualTo(defaultValue)
} }
} }

View File

@ -15,7 +15,7 @@ class PropertyValidationTest {
val property = Configuration.Property.Definition.long(key) val property = Configuration.Property.Definition.long(key)
assertThat(property.validate(configuration).errors).satisfies { errors -> assertThat(property.validate(configuration, Configuration.Options.defaults).errors).satisfies { errors ->
assertThat(errors).hasSize(1) assertThat(errors).hasSize(1)
assertThat(errors.first()).isInstanceOfSatisfying(Configuration.Validation.Error.MissingValue::class.java) { error -> assertThat(errors.first()).isInstanceOfSatisfying(Configuration.Validation.Error.MissingValue::class.java) { error ->
@ -34,7 +34,7 @@ class PropertyValidationTest {
val property = Configuration.Property.Definition.long(key) val property = Configuration.Property.Definition.long(key)
assertThat(property.validate(configuration).errors).satisfies { errors -> assertThat(property.validate(configuration, Configuration.Options.defaults).errors).satisfies { errors ->
assertThat(errors).hasSize(1) assertThat(errors).hasSize(1)
assertThat(errors.first()).isInstanceOfSatisfying(Configuration.Validation.Error.MissingValue::class.java) { error -> assertThat(errors.first()).isInstanceOfSatisfying(Configuration.Validation.Error.MissingValue::class.java) { error ->
@ -53,7 +53,7 @@ class PropertyValidationTest {
val property = Configuration.Property.Definition.long(key).list() val property = Configuration.Property.Definition.long(key).list()
assertThat(property.validate(configuration).errors).satisfies { errors -> assertThat(property.validate(configuration, Configuration.Options.defaults).errors).satisfies { errors ->
assertThat(errors).hasSize(1) assertThat(errors).hasSize(1)
assertThat(errors.first()).isInstanceOfSatisfying(Configuration.Validation.Error.MissingValue::class.java) { error -> assertThat(errors.first()).isInstanceOfSatisfying(Configuration.Validation.Error.MissingValue::class.java) { error ->
@ -72,7 +72,7 @@ class PropertyValidationTest {
val property = Configuration.Property.Definition.long(key).list() val property = Configuration.Property.Definition.long(key).list()
assertThat(property.validate(configuration).errors).satisfies { errors -> assertThat(property.validate(configuration, Configuration.Options.defaults).errors).satisfies { errors ->
assertThat(errors).hasSize(1) assertThat(errors).hasSize(1)
assertThat(errors.first()).isInstanceOfSatisfying(Configuration.Validation.Error.MissingValue::class.java) { error -> assertThat(errors.first()).isInstanceOfSatisfying(Configuration.Validation.Error.MissingValue::class.java) { error ->
@ -94,7 +94,7 @@ class PropertyValidationTest {
val property = Configuration.Property.Definition.long(key).list().mapValid(::parseMax) val property = Configuration.Property.Definition.long(key).list().mapValid(::parseMax)
assertThat(property.validate(configuration).errors).isEmpty() assertThat(property.validate(configuration, Configuration.Options.defaults).errors).isEmpty()
} }
@Test(timeout=300_000) @Test(timeout=300_000)
@ -114,7 +114,7 @@ class PropertyValidationTest {
val property = Configuration.Property.Definition.long(key).list().mapValid(::parseMax) val property = Configuration.Property.Definition.long(key).list().mapValid(::parseMax)
assertThat(property.validate(configuration).errors).satisfies { errors -> assertThat(property.validate(configuration, Configuration.Options.defaults).errors).satisfies { errors ->
assertThat(errors).hasSize(1) assertThat(errors).hasSize(1)
assertThat(errors.first()).isInstanceOfSatisfying(Configuration.Validation.Error.BadValue::class.java) { error -> assertThat(errors.first()).isInstanceOfSatisfying(Configuration.Validation.Error.BadValue::class.java) { error ->
@ -134,7 +134,7 @@ class PropertyValidationTest {
val configuration = configObject(key to false).toConfig() val configuration = configObject(key to false).toConfig()
assertThat(property.validate(configuration).errors).satisfies { errors -> assertThat(property.validate(configuration, Configuration.Options.defaults).errors).satisfies { errors ->
assertThat(errors).hasSize(1) assertThat(errors).hasSize(1)
assertThat(errors.first()).isInstanceOfSatisfying(Configuration.Validation.Error.WrongType::class.java) { error -> assertThat(errors.first()).isInstanceOfSatisfying(Configuration.Validation.Error.WrongType::class.java) { error ->
@ -154,7 +154,7 @@ class PropertyValidationTest {
val configuration = configObject(key to 1.2).toConfig() val configuration = configObject(key to 1.2).toConfig()
assertThat(property.validate(configuration).errors).satisfies { errors -> assertThat(property.validate(configuration, Configuration.Options.defaults).errors).satisfies { errors ->
assertThat(errors).hasSize(1) assertThat(errors).hasSize(1)
assertThat(errors.first()).isInstanceOfSatisfying(Configuration.Validation.Error.WrongType::class.java) { error -> assertThat(errors.first()).isInstanceOfSatisfying(Configuration.Validation.Error.WrongType::class.java) { error ->
@ -174,7 +174,7 @@ class PropertyValidationTest {
val configuration = configObject(key to 1).toConfig() val configuration = configObject(key to 1).toConfig()
assertThat(property.validate(configuration).isValid).isTrue() assertThat(property.validate(configuration, Configuration.Options.defaults).isValid).isTrue()
} }
@Test(timeout=300_000) @Test(timeout=300_000)
@ -186,7 +186,7 @@ class PropertyValidationTest {
val configuration = configObject(key to listOf(false, true)).toConfig() val configuration = configObject(key to listOf(false, true)).toConfig()
assertThat(property.validate(configuration).errors).satisfies { errors -> assertThat(property.validate(configuration, Configuration.Options.defaults).errors).satisfies { errors ->
assertThat(errors).hasSize(1) assertThat(errors).hasSize(1)
assertThat(errors.first()).isInstanceOfSatisfying(Configuration.Validation.Error.WrongType::class.java) { error -> assertThat(errors.first()).isInstanceOfSatisfying(Configuration.Validation.Error.WrongType::class.java) { error ->
@ -206,7 +206,7 @@ class PropertyValidationTest {
val configuration = configObject(key to listOf(1, 2, 3)).toConfig() val configuration = configObject(key to listOf(1, 2, 3)).toConfig()
assertThat(property.validate(configuration).errors).satisfies { errors -> assertThat(property.validate(configuration, Configuration.Options.defaults).errors).satisfies { errors ->
assertThat(errors).hasSize(1) assertThat(errors).hasSize(1)
assertThat(errors.first()).isInstanceOfSatisfying(Configuration.Validation.Error.WrongType::class.java) { error -> assertThat(errors.first()).isInstanceOfSatisfying(Configuration.Validation.Error.WrongType::class.java) { error ->
@ -226,7 +226,7 @@ class PropertyValidationTest {
val configuration = configObject(key to 1).toConfig() val configuration = configObject(key to 1).toConfig()
assertThat(property.validate(configuration).errors).satisfies { errors -> assertThat(property.validate(configuration, Configuration.Options.defaults).errors).satisfies { errors ->
assertThat(errors).hasSize(1) assertThat(errors).hasSize(1)
assertThat(errors.first()).isInstanceOfSatisfying(Configuration.Validation.Error.WrongType::class.java) { error -> assertThat(errors.first()).isInstanceOfSatisfying(Configuration.Validation.Error.WrongType::class.java) { error ->
@ -249,7 +249,7 @@ class PropertyValidationTest {
val configuration = configObject(key to configObject(nestedKey to false)).toConfig() val configuration = configObject(key to configObject(nestedKey to false)).toConfig()
assertThat(property.validate(configuration).errors).satisfies { errors -> assertThat(property.validate(configuration, Configuration.Options.defaults).errors).satisfies { errors ->
assertThat(errors).hasSize(1) assertThat(errors).hasSize(1)
assertThat(errors.first()).isInstanceOfSatisfying(Configuration.Validation.Error.WrongType::class.java) { error -> assertThat(errors.first()).isInstanceOfSatisfying(Configuration.Validation.Error.WrongType::class.java) { error ->
@ -272,7 +272,7 @@ class PropertyValidationTest {
val configuration = configObject(key to configObject()).toConfig() val configuration = configObject(key to configObject()).toConfig()
assertThat(property.validate(configuration).errors).satisfies { errors -> assertThat(property.validate(configuration, Configuration.Options.defaults).errors).satisfies { errors ->
assertThat(errors).hasSize(1) assertThat(errors).hasSize(1)
assertThat(errors.first()).isInstanceOfSatisfying(Configuration.Validation.Error.MissingValue::class.java) { error -> assertThat(errors.first()).isInstanceOfSatisfying(Configuration.Validation.Error.MissingValue::class.java) { error ->
@ -295,7 +295,7 @@ class PropertyValidationTest {
val configuration = configObject(key to configObject(nestedKey to null)).toConfig() val configuration = configObject(key to configObject(nestedKey to null)).toConfig()
assertThat(property.validate(configuration).errors).satisfies { errors -> assertThat(property.validate(configuration, Configuration.Options.defaults).errors).satisfies { errors ->
assertThat(errors).hasSize(1) assertThat(errors).hasSize(1)
assertThat(errors.first()).isInstanceOfSatisfying(Configuration.Validation.Error.MissingValue::class.java) { error -> assertThat(errors.first()).isInstanceOfSatisfying(Configuration.Validation.Error.MissingValue::class.java) { error ->
@ -317,7 +317,7 @@ class PropertyValidationTest {
val configuration = configObject(key to configObject(nestedKey to false)).toConfig() val configuration = configObject(key to configObject(nestedKey to false)).toConfig()
assertThat(property.validate(configuration).isValid).isTrue() assertThat(property.validate(configuration, Configuration.Options.defaults).isValid).isTrue()
} }
@Test(timeout=300_000) @Test(timeout=300_000)
@ -333,7 +333,7 @@ class PropertyValidationTest {
val configuration = configObject(key to value).toConfig() val configuration = configObject(key to value).toConfig()
assertThat(property.validate(configuration).isValid).isTrue() assertThat(property.validate(configuration, Configuration.Options.defaults).isValid).isTrue()
} }
@Test(timeout=300_000) @Test(timeout=300_000)
@ -350,7 +350,7 @@ class PropertyValidationTest {
val configuration = configObject(key to value).toConfig() val configuration = configObject(key to value).toConfig()
val result = property.validate(configuration) val result = property.validate(configuration, Configuration.Options.defaults)
assertThat(result.errors).satisfies { errors -> assertThat(result.errors).satisfies { errors ->

View File

@ -29,7 +29,7 @@ class SchemaTest {
val fooConfigSchema = Configuration.Schema.withProperties(name = "Foo") { setOf(boolean("prop4"), double("prop5")) } val fooConfigSchema = Configuration.Schema.withProperties(name = "Foo") { setOf(boolean("prop4"), double("prop5")) }
val barConfigSchema = Configuration.Schema.withProperties(name = "Bar") { setOf(string(prop1), long(prop2), nestedObject("prop3", fooConfigSchema)) } val barConfigSchema = Configuration.Schema.withProperties(name = "Bar") { setOf(string(prop1), long(prop2), nestedObject("prop3", fooConfigSchema)) }
val result = barConfigSchema.validate(configuration) val result = barConfigSchema.validate(configuration, Configuration.Options.defaults)
println(barConfigSchema.description()) println(barConfigSchema.description())
assertThat(result.isValid).isTrue() assertThat(result.isValid).isTrue()
@ -59,17 +59,17 @@ class SchemaTest {
val fooConfigSchema = Configuration.Schema.withProperties { setOf(boolean("prop4"), double("prop5")) } val fooConfigSchema = Configuration.Schema.withProperties { setOf(boolean("prop4"), double("prop5")) }
val barConfigSchema = Configuration.Schema.withProperties { setOf(string(prop1), long(prop2), nestedObject("prop3", fooConfigSchema)) } val barConfigSchema = Configuration.Schema.withProperties { setOf(string(prop1), long(prop2), nestedObject("prop3", fooConfigSchema)) }
val strictErrors = barConfigSchema.validate(configuration, Configuration.Validation.Options(strict = true)).errors val strictErrors = barConfigSchema.validate(configuration, Configuration.Options(strict = true)).errors
assertThat(strictErrors).hasSize(2) assertThat(strictErrors).hasSize(2)
assertThat(strictErrors.filter { error -> error.keyName == "prop4" }).hasSize(1) assertThat(strictErrors.filter { error -> error.keyName == "prop4" }).hasSize(1)
assertThat(strictErrors.filter { error -> error.keyName == "prop6" }).hasSize(1) assertThat(strictErrors.filter { error -> error.keyName == "prop6" }).hasSize(1)
val errors = barConfigSchema.validate(configuration, Configuration.Validation.Options(strict = false)).errors val errors = barConfigSchema.validate(configuration, Configuration.Options(strict = false)).errors
assertThat(errors).isEmpty() assertThat(errors).isEmpty()
val errorsWithDefaultOptions = barConfigSchema.validate(configuration).errors val errorsWithDefaultOptions = barConfigSchema.validate(configuration, Configuration.Options.defaults).errors
assertThat(errorsWithDefaultOptions).isEmpty() assertThat(errorsWithDefaultOptions).isEmpty()
} }
@ -98,7 +98,7 @@ class SchemaTest {
val fooConfigSchema = Configuration.Schema.withProperties { setOf(boolean("prop4"), double("prop5")) } val fooConfigSchema = Configuration.Schema.withProperties { setOf(boolean("prop4"), double("prop5")) }
val barConfigSchema = Configuration.Schema.withProperties { setOf(string(prop1), long(prop2), nestedObject("prop3", fooConfigSchema)) } val barConfigSchema = Configuration.Schema.withProperties { setOf(string(prop1), long(prop2), nestedObject("prop3", fooConfigSchema)) }
val result = barConfigSchema.validate(configuration) val result = barConfigSchema.validate(configuration, Configuration.Options.defaults)
assertThat(result.isValid).isTrue() assertThat(result.isValid).isTrue()
} }
@ -127,7 +127,7 @@ class SchemaTest {
val fooConfigSchema = Configuration.Schema.withProperties { setOf(boolean("prop4"), double("prop5")) } val fooConfigSchema = Configuration.Schema.withProperties { setOf(boolean("prop4"), double("prop5")) }
val barConfigSchema = Configuration.Schema.withProperties { setOf(string(prop1), long(prop2), nestedObject("prop3", fooConfigSchema)) } val barConfigSchema = Configuration.Schema.withProperties { setOf(string(prop1), long(prop2), nestedObject("prop3", fooConfigSchema)) }
val errors = barConfigSchema.validate(configuration).errors val errors = barConfigSchema.validate(configuration, Configuration.Options.defaults).errors
errors.forEach(::println) errors.forEach(::println)
assertThat(errors).hasSize(2) assertThat(errors).hasSize(2)
@ -154,7 +154,7 @@ class SchemaTest {
val fooConfigSchema = Configuration.Schema.withProperties(name = "Foo") { setOf(boolean("prop4"), string("prop5", sensitive = true)) } val fooConfigSchema = Configuration.Schema.withProperties(name = "Foo") { setOf(boolean("prop4"), string("prop5", sensitive = true)) }
val barConfigSchema = Configuration.Schema.withProperties(name = "Bar") { setOf(string(prop1), long(prop2), nestedObject("prop3", fooConfigSchema)) } val barConfigSchema = Configuration.Schema.withProperties(name = "Bar") { setOf(string(prop1), long(prop2), nestedObject("prop3", fooConfigSchema)) }
val printedConfiguration = barConfigSchema.describe(configuration) val printedConfiguration = barConfigSchema.describe(configuration, options = Configuration.Options.defaults)
val description = printedConfiguration.serialize().also { println(it) } val description = printedConfiguration.serialize().also { println(it) }
@ -185,7 +185,7 @@ class SchemaTest {
val fooConfigSchema = Configuration.Schema.withProperties(name = "Foo") { setOf(boolean("prop4"), string("prop5", sensitive = true)) } val fooConfigSchema = Configuration.Schema.withProperties(name = "Foo") { setOf(boolean("prop4"), string("prop5", sensitive = true)) }
val barConfigSchema = Configuration.Schema.withProperties(name = "Bar") { setOf(string(prop1), long(prop2), nestedObject("prop3", fooConfigSchema).list()) } val barConfigSchema = Configuration.Schema.withProperties(name = "Bar") { setOf(string(prop1), long(prop2), nestedObject("prop3", fooConfigSchema).list()) }
val printedConfiguration = barConfigSchema.describe(configuration) val printedConfiguration = barConfigSchema.describe(configuration, options = Configuration.Options.defaults)
val description = printedConfiguration.serialize().also { println(it) } val description = printedConfiguration.serialize().also { println(it) }

View File

@ -1,7 +1,6 @@
package net.corda.common.configuration.parsing.internal package net.corda.common.configuration.parsing.internal
import com.typesafe.config.Config import com.typesafe.config.Config
import net.corda.common.validation.internal.Validated
import net.corda.common.validation.internal.Validated.Companion.invalid import net.corda.common.validation.internal.Validated.Companion.invalid
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.junit.Test import org.junit.Test
@ -16,7 +15,7 @@ class SpecificationTest {
val principal by string().mapValid(::parseAddress) val principal by string().mapValid(::parseAddress)
val admin by string().mapValid(::parseAddress) val admin by string().mapValid(::parseAddress)
override fun parseValid(configuration: Config) = valid(Addresses(configuration[principal], configuration[admin])) override fun parseValid(configuration: Config, options: Configuration.Options) = configuration.withOptions(options).let { valid(Addresses(it[principal], it[admin])) }
private fun parseAddress(rawValue: String): Valid<Address> { private fun parseAddress(rawValue: String): Valid<Address> {
@ -27,7 +26,7 @@ class SpecificationTest {
val useSsl by boolean() val useSsl by boolean()
val addresses by nested(AddressesSpec) val addresses by nested(AddressesSpec)
override fun parseValid(configuration: Config) = valid<RpcSettings>(RpcSettingsImpl(configuration[addresses], configuration[useSsl])) override fun parseValid(configuration: Config, options: Configuration.Options) = configuration.withOptions(options).let { valid<RpcSettings>(RpcSettingsImpl(it[addresses], it[useSsl])) }
} }
@Test(timeout=300_000) @Test(timeout=300_000)
@ -60,9 +59,9 @@ class SpecificationTest {
private val maxElement by long("elements").list().map { elements -> elements.max() } private val maxElement by long("elements").list().map { elements -> elements.max() }
override fun parseValid(configuration: Config): Valid<AtomicLong> { override fun parseValid(configuration: Config, options: Configuration.Options): Valid<AtomicLong> {
val config = configuration.withOptions(options)
return valid(AtomicLong(configuration[maxElement]!!)) return valid(AtomicLong(config[maxElement]!!))
} }
} }
@ -111,9 +110,9 @@ class SpecificationTest {
private val maxElement by long("elements").list().mapValid(::parseMax) private val maxElement by long("elements").list().mapValid(::parseMax)
override fun parseValid(configuration: Config): Valid<AtomicLong> { override fun parseValid(configuration: Config, options: Configuration.Options): Valid<AtomicLong> {
val config = configuration.withOptions(options)
return valid(AtomicLong(configuration[maxElement])) return valid(AtomicLong(config[maxElement]))
} }
} }
@ -159,7 +158,7 @@ class SpecificationTest {
@Suppress("unused") @Suppress("unused")
val myProp by string().list().optional() val myProp by string().list().optional()
override fun parseValid(configuration: Config) = valid(configuration[myProp]) override fun parseValid(configuration: Config, options: Configuration.Options) = configuration.withOptions(options).let { valid(it[myProp]) }
} }
assertThat(spec.properties).hasSize(1) assertThat(spec.properties).hasSize(1)

View File

@ -15,5 +15,5 @@ internal fun <VALUE> extractValueWithErrors(errors: Set<Configuration.Validation
internal fun <VALUE> extractValue(value: Valid<VALUE>) = object : Configuration.Value.Parser<VALUE> { internal fun <VALUE> extractValue(value: Valid<VALUE>) = object : Configuration.Value.Parser<VALUE> {
override fun parse(configuration: Config, options: Configuration.Validation.Options): Valid<VALUE> = value override fun parse(configuration: Config, options: Configuration.Options): Valid<VALUE> = value
} }

View File

@ -11,7 +11,7 @@ java8MinUpdateVersion=171
# When incrementing platformVersion make sure to update # # When incrementing platformVersion make sure to update #
# net.corda.core.internal.CordaUtilsKt.PLATFORM_VERSION as well. # # net.corda.core.internal.CordaUtilsKt.PLATFORM_VERSION as well. #
# ***************************************************************# # ***************************************************************#
platformVersion=6 platformVersion=7
guavaVersion=28.0-jre guavaVersion=28.0-jre
# Quasar version to use with Java 8: # Quasar version to use with Java 8:
quasarVersion=0.7.12_r3 quasarVersion=0.7.12_r3
@ -21,7 +21,7 @@ quasarVersion11=0.8.0_r3
jdkClassifier11=jdk11 jdkClassifier11=jdk11
proguardVersion=6.1.1 proguardVersion=6.1.1
bouncycastleVersion=1.60 bouncycastleVersion=1.60
classgraphVersion=4.8.58 classgraphVersion=4.8.68
disruptorVersion=3.4.2 disruptorVersion=3.4.2
typesafeConfigVersion=1.3.4 typesafeConfigVersion=1.3.4
jsr305Version=3.0.2 jsr305Version=3.0.2
@ -30,7 +30,7 @@ snakeYamlVersion=1.19
caffeineVersion=2.7.0 caffeineVersion=2.7.0
metricsVersion=4.1.0 metricsVersion=4.1.0
metricsNewRelicVersion=1.1.1 metricsNewRelicVersion=1.1.1
djvmVersion=1.0-RC10 djvmVersion=1.1-RC01
deterministicRtVersion=1.0-RC02 deterministicRtVersion=1.0-RC02
openSourceBranch=https://github.com/corda/corda/blob/release/os/4.4 openSourceBranch=https://github.com/corda/corda/blob/release/os/4.4
openSourceSamplesBranch=https://github.com/corda/samples/blob/release-V4 openSourceSamplesBranch=https://github.com/corda/samples/blob/release-V4

View File

@ -23,7 +23,6 @@ import java.util.concurrent.CompletableFuture;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import static net.corda.testing.driver.Driver.driver; import static net.corda.testing.driver.Driver.driver;
import static org.junit.Assert.assertEquals;
public class FlowExternalOperationInJavaTest extends AbstractFlowExternalOperationTest { public class FlowExternalOperationInJavaTest extends AbstractFlowExternalOperationTest {
@ -32,16 +31,16 @@ public class FlowExternalOperationInJavaTest extends AbstractFlowExternalOperati
driver(new DriverParameters().withStartNodesInProcess(true), driver -> { driver(new DriverParameters().withStartNodesInProcess(true), driver -> {
NodeHandle alice = KotlinUtilsKt.getOrThrow( NodeHandle alice = KotlinUtilsKt.getOrThrow(
driver.startNode(new NodeParameters().withProvidedName(TestConstants.ALICE_NAME)), driver.startNode(new NodeParameters().withProvidedName(TestConstants.ALICE_NAME)),
Duration.of(20, ChronoUnit.SECONDS) Duration.of(1, ChronoUnit.MINUTES)
); );
NodeHandle bob = KotlinUtilsKt.getOrThrow( NodeHandle bob = KotlinUtilsKt.getOrThrow(
driver.startNode(new NodeParameters().withProvidedName(TestConstants.BOB_NAME)), driver.startNode(new NodeParameters().withProvidedName(TestConstants.BOB_NAME)),
Duration.of(20, ChronoUnit.SECONDS) Duration.of(1, ChronoUnit.MINUTES)
); );
return KotlinUtilsKt.getOrThrow(alice.getRpc().startFlowDynamic( return KotlinUtilsKt.getOrThrow(alice.getRpc().startFlowDynamic(
FlowWithExternalOperationInJava.class, FlowWithExternalOperationInJava.class,
TestUtils.singleIdentity(bob.getNodeInfo()) TestUtils.singleIdentity(bob.getNodeInfo())
).getReturnValue(), Duration.of(20, ChronoUnit.SECONDS)); ).getReturnValue(), Duration.of(1, ChronoUnit.MINUTES));
}); });
} }
@ -50,16 +49,16 @@ public class FlowExternalOperationInJavaTest extends AbstractFlowExternalOperati
driver(new DriverParameters().withStartNodesInProcess(true), driver -> { driver(new DriverParameters().withStartNodesInProcess(true), driver -> {
NodeHandle alice = KotlinUtilsKt.getOrThrow( NodeHandle alice = KotlinUtilsKt.getOrThrow(
driver.startNode(new NodeParameters().withProvidedName(TestConstants.ALICE_NAME)), driver.startNode(new NodeParameters().withProvidedName(TestConstants.ALICE_NAME)),
Duration.of(20, ChronoUnit.SECONDS) Duration.of(1, ChronoUnit.MINUTES)
); );
NodeHandle bob = KotlinUtilsKt.getOrThrow( NodeHandle bob = KotlinUtilsKt.getOrThrow(
driver.startNode(new NodeParameters().withProvidedName(TestConstants.BOB_NAME)), driver.startNode(new NodeParameters().withProvidedName(TestConstants.BOB_NAME)),
Duration.of(20, ChronoUnit.SECONDS) Duration.of(1, ChronoUnit.MINUTES)
); );
return KotlinUtilsKt.getOrThrow(alice.getRpc().startFlowDynamic( return KotlinUtilsKt.getOrThrow(alice.getRpc().startFlowDynamic(
FlowWithExternalAsyncOperationInJava.class, FlowWithExternalAsyncOperationInJava.class,
TestUtils.singleIdentity(bob.getNodeInfo()) TestUtils.singleIdentity(bob.getNodeInfo())
).getReturnValue(), Duration.of(20, ChronoUnit.SECONDS)); ).getReturnValue(), Duration.of(1, ChronoUnit.MINUTES));
}); });
} }
@ -68,22 +67,18 @@ public class FlowExternalOperationInJavaTest extends AbstractFlowExternalOperati
driver(new DriverParameters().withStartNodesInProcess(true), driver -> { driver(new DriverParameters().withStartNodesInProcess(true), driver -> {
NodeHandle alice = KotlinUtilsKt.getOrThrow( NodeHandle alice = KotlinUtilsKt.getOrThrow(
driver.startNode(new NodeParameters().withProvidedName(TestConstants.ALICE_NAME)), driver.startNode(new NodeParameters().withProvidedName(TestConstants.ALICE_NAME)),
Duration.of(20, ChronoUnit.SECONDS) Duration.of(1, ChronoUnit.MINUTES)
); );
NodeHandle bob = KotlinUtilsKt.getOrThrow( NodeHandle bob = KotlinUtilsKt.getOrThrow(
driver.startNode(new NodeParameters().withProvidedName(TestConstants.BOB_NAME)), driver.startNode(new NodeParameters().withProvidedName(TestConstants.BOB_NAME)),
Duration.of(20, ChronoUnit.SECONDS) Duration.of(1, ChronoUnit.MINUTES)
); );
KotlinUtilsKt.getOrThrow(alice.getRpc().startFlowDynamic( KotlinUtilsKt.getOrThrow(alice.getRpc().startFlowDynamic(
FlowWithExternalOperationThatGetsRetriedInJava.class, FlowWithExternalOperationThatGetsRetriedInJava.class,
TestUtils.singleIdentity(bob.getNodeInfo()) TestUtils.singleIdentity(bob.getNodeInfo())
).getReturnValue(), Duration.of(20, ChronoUnit.SECONDS)); ).getReturnValue(), Duration.of(1, ChronoUnit.MINUTES));
HospitalCounts counts = KotlinUtilsKt.getOrThrow(alice.getRpc().startFlowDynamic( assertHospitalCounters(1, 0);
GetHospitalCountersFlow.class
).getReturnValue(), Duration.of(20, ChronoUnit.SECONDS));
assertEquals(1, counts.getDischarge());
assertEquals(0, counts.getObservation());
return null; return null;
}); });

View File

@ -12,26 +12,55 @@ import net.corda.core.flows.StartableByRPC
import net.corda.core.flows.StartableByService import net.corda.core.flows.StartableByService
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.internal.concurrent.doOnComplete import net.corda.core.internal.concurrent.doOnComplete
import net.corda.core.messaging.FlowHandle
import net.corda.core.node.AppServiceHub import net.corda.core.node.AppServiceHub
import net.corda.core.node.ServiceHub import net.corda.core.node.ServiceHub
import net.corda.core.node.services.CordaService import net.corda.core.node.services.CordaService
import net.corda.core.schemas.MappedSchema import net.corda.core.schemas.MappedSchema
import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.core.utilities.contextLogger import net.corda.core.utilities.contextLogger
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.unwrap
import net.corda.node.services.statemachine.StaffedFlowHospital import net.corda.node.services.statemachine.StaffedFlowHospital
import org.junit.Before
import java.sql.SQLTransientConnectionException import java.sql.SQLTransientConnectionException
import java.util.concurrent.CompletableFuture import java.util.concurrent.CompletableFuture
import java.util.concurrent.Executors import java.util.concurrent.Executors
import java.util.concurrent.Semaphore
import java.util.function.Supplier import java.util.function.Supplier
import javax.persistence.Column import javax.persistence.Column
import javax.persistence.Entity import javax.persistence.Entity
import javax.persistence.Id import javax.persistence.Id
import javax.persistence.Table import javax.persistence.Table
import kotlin.test.assertEquals
abstract class AbstractFlowExternalOperationTest { abstract class AbstractFlowExternalOperationTest {
var dischargeCounter = 0
var observationCounter = 0
@Before
fun before() {
StaffedFlowHospital.onFlowDischarged.clear()
StaffedFlowHospital.onFlowDischarged.add { _, _ -> ++dischargeCounter }
StaffedFlowHospital.onFlowKeptForOvernightObservation.clear()
StaffedFlowHospital.onFlowKeptForOvernightObservation.add { _, _ -> ++observationCounter }
dischargeCounter = 0
observationCounter = 0
}
fun blockUntilFlowKeptInForObservation(flow: () -> FlowHandle<*>) {
val lock = Semaphore(0)
StaffedFlowHospital.onFlowKeptForOvernightObservation.add { _, _ -> lock.release() }
flow()
lock.acquire()
}
fun assertHospitalCounters(discharge: Int, observation: Int) {
assertEquals(discharge, dischargeCounter)
assertEquals(observation, observationCounter)
}
@StartableByRPC @StartableByRPC
@InitiatingFlow @InitiatingFlow
@StartableByService @StartableByService
@ -44,11 +73,12 @@ abstract class AbstractFlowExternalOperationTest {
@Suspendable @Suspendable
override fun call(): Any { override fun call(): Any {
log.info("Started my flow") log.info("Started my flow")
subFlow(PingPongFlow(party))
val result = testCode() val result = testCode()
val session = initiateFlow(party) val session = initiateFlow(party)
session.send("hi there") session.sendAndReceive<String>("hi there").unwrap { it }
log.info("ServiceHub value = $serviceHub") session.sendAndReceive<String>("hi there").unwrap { it }
session.receive<String>() subFlow(PingPongFlow(party))
log.info("Finished my flow") log.info("Finished my flow")
return result return result
} }
@ -64,8 +94,28 @@ abstract class AbstractFlowExternalOperationTest {
class FlowWithExternalOperationResponder(val session: FlowSession) : FlowLogic<Unit>() { class FlowWithExternalOperationResponder(val session: FlowSession) : FlowLogic<Unit>() {
@Suspendable @Suspendable
override fun call() { override fun call() {
session.receive<String>() session.receive<String>().unwrap { it }
session.send("go away") session.send("go away")
session.receive<String>().unwrap { it }
session.send("go away")
}
}
@InitiatingFlow
class PingPongFlow(val party: Party): FlowLogic<Unit>() {
@Suspendable
override fun call() {
val session = initiateFlow(party)
session.sendAndReceive<String>("ping pong").unwrap { it }
}
}
@InitiatedBy(PingPongFlow::class)
class PingPongResponder(val session: FlowSession) : FlowLogic<Unit>() {
@Suspendable
override fun call() {
session.receive<String>().unwrap { it }
session.send("I got you bro")
} }
} }
@ -83,7 +133,7 @@ abstract class AbstractFlowExternalOperationTest {
fun createFuture(): CompletableFuture<Any> { fun createFuture(): CompletableFuture<Any> {
return CompletableFuture.supplyAsync(Supplier<Any> { return CompletableFuture.supplyAsync(Supplier<Any> {
log.info("Starting sleep inside of future") log.info("Starting sleep inside of future")
Thread.sleep(2000) Thread.sleep(1000)
log.info("Finished sleep inside of future") log.info("Finished sleep inside of future")
"Here is your return value" "Here is your return value"
}, executorService) }, executorService)
@ -182,31 +232,6 @@ abstract class AbstractFlowExternalOperationTest {
object CustomMappedSchema : MappedSchema(CustomSchema::class.java, 1, listOf(CustomTableEntity::class.java)) object CustomMappedSchema : MappedSchema(CustomSchema::class.java, 1, listOf(CustomTableEntity::class.java))
// Internal use for testing only!!
@StartableByRPC
class GetHospitalCountersFlow : FlowLogic<HospitalCounts>() {
override fun call(): HospitalCounts =
HospitalCounts(
serviceHub.cordaService(HospitalCounter::class.java).dischargeCounter,
serviceHub.cordaService(HospitalCounter::class.java).observationCounter
)
}
@CordaSerializable
data class HospitalCounts(val discharge: Int, val observation: Int)
@Suppress("UNUSED_PARAMETER")
@CordaService
class HospitalCounter(services: AppServiceHub) : SingletonSerializeAsToken() {
var observationCounter: Int = 0
var dischargeCounter: Int = 0
init {
StaffedFlowHospital.onFlowDischarged.add { _, _ -> ++dischargeCounter }
StaffedFlowHospital.onFlowKeptForOvernightObservation.add { _, _ -> ++observationCounter }
}
}
class MyCordaException(message: String) : CordaException(message) class MyCordaException(message: String) : CordaException(message)
class DirectlyAccessedServiceHubException : CordaException("Null pointer from accessing flow's serviceHub") class DirectlyAccessedServiceHubException : CordaException("Null pointer from accessing flow's serviceHub")

View File

@ -6,12 +6,7 @@ import net.corda.core.flows.StartableByRPC
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.messaging.startFlow import net.corda.core.messaging.startFlow
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.seconds import net.corda.core.utilities.minutes
import net.corda.coretests.flows.AbstractFlowExternalOperationTest.DirectlyAccessedServiceHubException
import net.corda.coretests.flows.AbstractFlowExternalOperationTest.ExternalAsyncOperation
import net.corda.coretests.flows.AbstractFlowExternalOperationTest.FlowWithExternalProcess
import net.corda.coretests.flows.AbstractFlowExternalOperationTest.FutureService
import net.corda.coretests.flows.AbstractFlowExternalOperationTest.MyCordaException
import net.corda.node.services.statemachine.StateTransitionException import net.corda.node.services.statemachine.StateTransitionException
import net.corda.testing.core.ALICE_NAME import net.corda.testing.core.ALICE_NAME
import net.corda.testing.core.BOB_NAME import net.corda.testing.core.BOB_NAME
@ -21,27 +16,23 @@ import net.corda.testing.driver.driver
import org.junit.Test import org.junit.Test
import java.sql.SQLTransientConnectionException import java.sql.SQLTransientConnectionException
import java.util.concurrent.CompletableFuture import java.util.concurrent.CompletableFuture
import java.util.concurrent.TimeoutException
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith import kotlin.test.assertFailsWith
import kotlin.test.assertTrue import kotlin.test.assertTrue
class FlowExternalAsyncOperationTest : AbstractFlowExternalOperationTest() { class FlowExternalAsyncOperationTest : AbstractFlowExternalOperationTest() {
@Test(timeout=300_000) @Test(timeout = 300_000)
fun `external async operation`() { fun `external async operation`() {
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) { driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
val alice = startNode(providedName = ALICE_NAME).getOrThrow() val alice = startNode(providedName = ALICE_NAME).getOrThrow()
val bob = startNode(providedName = BOB_NAME).getOrThrow() val bob = startNode(providedName = BOB_NAME).getOrThrow()
alice.rpc.startFlow(::FlowWithExternalAsyncOperation, bob.nodeInfo.singleIdentity()) alice.rpc.startFlow(::FlowWithExternalAsyncOperation, bob.nodeInfo.singleIdentity())
.returnValue.getOrThrow(20.seconds) .returnValue.getOrThrow(1.minutes)
val (discharged, observation) = alice.rpc.startFlow(::GetHospitalCountersFlow).returnValue.getOrThrow() assertHospitalCounters(0, 0)
assertEquals(0, discharged)
assertEquals(0, observation)
} }
} }
@Test(timeout=300_000) @Test(timeout = 300_000)
fun `external async operation that checks deduplicationId is not rerun when flow is retried`() { fun `external async operation that checks deduplicationId is not rerun when flow is retried`() {
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) { driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
val alice = startNode(providedName = ALICE_NAME).getOrThrow() val alice = startNode(providedName = ALICE_NAME).getOrThrow()
@ -50,15 +41,13 @@ class FlowExternalAsyncOperationTest : AbstractFlowExternalOperationTest() {
alice.rpc.startFlow( alice.rpc.startFlow(
::FlowWithExternalAsyncOperationWithDeduplication, ::FlowWithExternalAsyncOperationWithDeduplication,
bob.nodeInfo.singleIdentity() bob.nodeInfo.singleIdentity()
).returnValue.getOrThrow(20.seconds) ).returnValue.getOrThrow(1.minutes)
} }
val (discharged, observation) = alice.rpc.startFlow(::GetHospitalCountersFlow).returnValue.getOrThrow() assertHospitalCounters(1, 0)
assertEquals(1, discharged)
assertEquals(0, observation)
} }
} }
@Test(timeout=300_000) @Test(timeout = 300_000)
fun `external async operation propagates exception to calling flow`() { fun `external async operation propagates exception to calling flow`() {
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) { driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
val alice = startNode(providedName = ALICE_NAME).getOrThrow() val alice = startNode(providedName = ALICE_NAME).getOrThrow()
@ -68,15 +57,13 @@ class FlowExternalAsyncOperationTest : AbstractFlowExternalOperationTest() {
::FlowWithExternalAsyncOperationPropagatesException, ::FlowWithExternalAsyncOperationPropagatesException,
bob.nodeInfo.singleIdentity(), bob.nodeInfo.singleIdentity(),
MyCordaException::class.java MyCordaException::class.java
).returnValue.getOrThrow(20.seconds) ).returnValue.getOrThrow(1.minutes)
} }
val (discharged, observation) = alice.rpc.startFlow(::GetHospitalCountersFlow).returnValue.getOrThrow() assertHospitalCounters(0, 0)
assertEquals(0, discharged)
assertEquals(0, observation)
} }
} }
@Test(timeout=300_000) @Test(timeout = 300_000)
fun `external async operation exception can be caught in flow`() { fun `external async operation exception can be caught in flow`() {
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) { driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
val alice = startNode(providedName = ALICE_NAME).getOrThrow() val alice = startNode(providedName = ALICE_NAME).getOrThrow()
@ -84,83 +71,73 @@ class FlowExternalAsyncOperationTest : AbstractFlowExternalOperationTest() {
val result = alice.rpc.startFlow( val result = alice.rpc.startFlow(
::FlowWithExternalAsyncOperationThatThrowsExceptionAndCaughtInFlow, ::FlowWithExternalAsyncOperationThatThrowsExceptionAndCaughtInFlow,
bob.nodeInfo.singleIdentity() bob.nodeInfo.singleIdentity()
).returnValue.getOrThrow(20.seconds) ).returnValue.getOrThrow(1.minutes)
assertTrue(result as Boolean) assertTrue(result as Boolean)
val (discharged, observation) = alice.rpc.startFlow(::GetHospitalCountersFlow).returnValue.getOrThrow() assertHospitalCounters(0, 0)
assertEquals(0, discharged)
assertEquals(0, observation)
} }
} }
@Test(timeout=300_000) @Test(timeout = 300_000)
fun `external async operation with exception that hospital keeps for observation does not fail`() { fun `external async operation with exception that hospital keeps for observation does not fail`() {
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) { driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
val alice = startNode(providedName = ALICE_NAME).getOrThrow() val alice = startNode(providedName = ALICE_NAME).getOrThrow()
val bob = startNode(providedName = BOB_NAME).getOrThrow() val bob = startNode(providedName = BOB_NAME).getOrThrow()
assertFailsWith<TimeoutException> { blockUntilFlowKeptInForObservation {
alice.rpc.startFlow( alice.rpc.startFlow(
::FlowWithExternalAsyncOperationPropagatesException, ::FlowWithExternalAsyncOperationPropagatesException,
bob.nodeInfo.singleIdentity(), bob.nodeInfo.singleIdentity(),
HospitalizeFlowException::class.java HospitalizeFlowException::class.java
).returnValue.getOrThrow(20.seconds) )
} }
val (discharged, observation) = alice.rpc.startFlow(::GetHospitalCountersFlow).returnValue.getOrThrow() assertHospitalCounters(0, 1)
assertEquals(0, discharged)
assertEquals(1, observation)
} }
} }
@Test(timeout=300_000) @Test(timeout = 300_000)
fun `external async operation with exception that hospital discharges is retried and runs the future again`() { fun `external async operation with exception that hospital discharges is retried and runs the future again`() {
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) { driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
val alice = startNode(providedName = ALICE_NAME).getOrThrow() val alice = startNode(providedName = ALICE_NAME).getOrThrow()
val bob = startNode(providedName = BOB_NAME).getOrThrow() val bob = startNode(providedName = BOB_NAME).getOrThrow()
assertFailsWith<TimeoutException> { blockUntilFlowKeptInForObservation {
alice.rpc.startFlow( alice.rpc.startFlow(
::FlowWithExternalAsyncOperationPropagatesException, ::FlowWithExternalAsyncOperationPropagatesException,
bob.nodeInfo.singleIdentity(), bob.nodeInfo.singleIdentity(),
SQLTransientConnectionException::class.java SQLTransientConnectionException::class.java
).returnValue.getOrThrow(20.seconds) )
} }
val (discharged, observation) = alice.rpc.startFlow(::GetHospitalCountersFlow).returnValue.getOrThrow() assertHospitalCounters(3, 1)
assertEquals(3, discharged)
assertEquals(1, observation)
} }
} }
@Test(timeout=300_000) @Test(timeout = 300_000)
fun `external async operation that throws exception rather than completing future exceptionally fails with internal exception`() { fun `external async operation that throws exception rather than completing future exceptionally fails with internal exception`() {
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) { driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
val alice = startNode(providedName = ALICE_NAME).getOrThrow() val alice = startNode(providedName = ALICE_NAME).getOrThrow()
val bob = startNode(providedName = BOB_NAME).getOrThrow() val bob = startNode(providedName = BOB_NAME).getOrThrow()
assertFailsWith<StateTransitionException> { assertFailsWith<StateTransitionException> {
alice.rpc.startFlow(::FlowWithExternalAsyncOperationUnhandledException, bob.nodeInfo.singleIdentity()) alice.rpc.startFlow(::FlowWithExternalAsyncOperationUnhandledException, bob.nodeInfo.singleIdentity())
.returnValue.getOrThrow(20.seconds) .returnValue.getOrThrow(1.minutes)
} }
val (discharged, observation) = alice.rpc.startFlow(::GetHospitalCountersFlow).returnValue.getOrThrow() assertHospitalCounters(0, 0)
assertEquals(0, discharged)
assertEquals(0, observation)
} }
} }
@Test(timeout=300_000) @Test(timeout = 300_000)
fun `external async operation that passes serviceHub into process can be retried`() { fun `external async operation that passes serviceHub into process can be retried`() {
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) { driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
val alice = startNode(providedName = ALICE_NAME).getOrThrow() val alice = startNode(providedName = ALICE_NAME).getOrThrow()
val bob = startNode(providedName = BOB_NAME).getOrThrow() val bob = startNode(providedName = BOB_NAME).getOrThrow()
assertFailsWith<TimeoutException> { blockUntilFlowKeptInForObservation {
alice.rpc.startFlow( alice.rpc.startFlow(
::FlowWithExternalAsyncOperationThatPassesInServiceHubCanRetry, ::FlowWithExternalAsyncOperationThatPassesInServiceHubCanRetry,
bob.nodeInfo.singleIdentity() bob.nodeInfo.singleIdentity()
).returnValue.getOrThrow(20.seconds) )
} }
val (discharged, observation) = alice.rpc.startFlow(::GetHospitalCountersFlow).returnValue.getOrThrow() assertHospitalCounters(3, 1)
assertEquals(3, discharged)
assertEquals(1, observation)
} }
} }
@Test(timeout=300_000) @Test(timeout = 300_000)
fun `external async operation that accesses serviceHub from flow directly will fail when retried`() { fun `external async operation that accesses serviceHub from flow directly will fail when retried`() {
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) { driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
val alice = startNode(providedName = ALICE_NAME).getOrThrow() val alice = startNode(providedName = ALICE_NAME).getOrThrow()
@ -169,23 +146,19 @@ class FlowExternalAsyncOperationTest : AbstractFlowExternalOperationTest() {
alice.rpc.startFlow( alice.rpc.startFlow(
::FlowWithExternalAsyncOperationThatDirectlyAccessesServiceHubFailsRetry, ::FlowWithExternalAsyncOperationThatDirectlyAccessesServiceHubFailsRetry,
bob.nodeInfo.singleIdentity() bob.nodeInfo.singleIdentity()
).returnValue.getOrThrow(20.seconds) ).returnValue.getOrThrow(1.minutes)
} }
val (discharged, observation) = alice.rpc.startFlow(::GetHospitalCountersFlow).returnValue.getOrThrow() assertHospitalCounters(1, 0)
assertEquals(1, discharged)
assertEquals(0, observation)
} }
} }
@Test(timeout=300_000) @Test(timeout = 300_000)
fun `starting multiple futures and joining on their results`() { fun `starting multiple futures and joining on their results`() {
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) { driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
val alice = startNode(providedName = ALICE_NAME).getOrThrow() val alice = startNode(providedName = ALICE_NAME).getOrThrow()
val bob = startNode(providedName = BOB_NAME).getOrThrow() val bob = startNode(providedName = BOB_NAME).getOrThrow()
alice.rpc.startFlow(::FlowThatStartsMultipleFuturesAndJoins, bob.nodeInfo.singleIdentity()).returnValue.getOrThrow(20.seconds) alice.rpc.startFlow(::FlowThatStartsMultipleFuturesAndJoins, bob.nodeInfo.singleIdentity()).returnValue.getOrThrow(1.minutes)
val (discharged, observation) = alice.rpc.startFlow(::GetHospitalCountersFlow).returnValue.getOrThrow() assertHospitalCounters(0, 0)
assertEquals(0, discharged)
assertEquals(0, observation)
} }
} }
@ -204,12 +177,14 @@ class FlowExternalAsyncOperationTest : AbstractFlowExternalOperationTest() {
FlowWithExternalProcess(party) { FlowWithExternalProcess(party) {
@Suspendable @Suspendable
override fun testCode(): Any = override fun testCode(): Any {
await(ExternalAsyncOperation(serviceHub) { _, _ -> val e = createException()
return await(ExternalAsyncOperation(serviceHub) { _, _ ->
CompletableFuture<Any>().apply { CompletableFuture<Any>().apply {
completeExceptionally(createException()) completeExceptionally(e)
} }
}) })
}
private fun createException() = when (exceptionType) { private fun createException() = when (exceptionType) {
HospitalizeFlowException::class.java -> HospitalizeFlowException("keep it around") HospitalizeFlowException::class.java -> HospitalizeFlowException("keep it around")

View File

@ -5,40 +5,35 @@ import net.corda.core.flows.StartableByRPC
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.messaging.startFlow import net.corda.core.messaging.startFlow
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.seconds import net.corda.core.utilities.minutes
import net.corda.testing.core.ALICE_NAME import net.corda.testing.core.ALICE_NAME
import net.corda.testing.core.BOB_NAME import net.corda.testing.core.BOB_NAME
import net.corda.testing.core.singleIdentity import net.corda.testing.core.singleIdentity
import net.corda.testing.driver.DriverParameters import net.corda.testing.driver.DriverParameters
import net.corda.testing.driver.driver import net.corda.testing.driver.driver
import org.junit.Test import org.junit.Test
import kotlin.test.assertEquals
class FlowExternalOperationStartFlowTest : AbstractFlowExternalOperationTest() { class FlowExternalOperationStartFlowTest : AbstractFlowExternalOperationTest() {
@Test(timeout=300_000) @Test(timeout = 300_000)
fun `starting a flow inside of a flow that starts a future will succeed`() { fun `starting a flow inside of a flow that starts a future will succeed`() {
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) { driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
val alice = startNode(providedName = ALICE_NAME).getOrThrow() val alice = startNode(providedName = ALICE_NAME).getOrThrow()
val bob = startNode(providedName = BOB_NAME).getOrThrow() val bob = startNode(providedName = BOB_NAME).getOrThrow()
alice.rpc.startFlow(::FlowThatStartsAnotherFlowInAnExternalOperation, bob.nodeInfo.singleIdentity()) alice.rpc.startFlow(::FlowThatStartsAnotherFlowInAnExternalOperation, bob.nodeInfo.singleIdentity())
.returnValue.getOrThrow(40.seconds) .returnValue.getOrThrow(1.minutes)
val (discharged, observation) = alice.rpc.startFlow(::GetHospitalCountersFlow).returnValue.getOrThrow() assertHospitalCounters(0, 0)
assertEquals(0, discharged)
assertEquals(0, observation)
} }
} }
@Test(timeout=300_000) @Test(timeout = 300_000)
fun `multiple flows can be started and their futures joined from inside a flow`() { fun `multiple flows can be started and their futures joined from inside a flow`() {
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) { driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
val alice = startNode(providedName = ALICE_NAME).getOrThrow() val alice = startNode(providedName = ALICE_NAME).getOrThrow()
val bob = startNode(providedName = BOB_NAME).getOrThrow() val bob = startNode(providedName = BOB_NAME).getOrThrow()
alice.rpc.startFlow(::ForkJoinFlows, bob.nodeInfo.singleIdentity()) alice.rpc.startFlow(::ForkJoinFlows, bob.nodeInfo.singleIdentity())
.returnValue.getOrThrow(40.seconds) .returnValue.getOrThrow(1.minutes)
val (discharged, observation) = alice.rpc.startFlow(::GetHospitalCountersFlow).returnValue.getOrThrow() assertHospitalCounters(0, 0)
assertEquals(0, discharged)
assertEquals(0, observation)
} }
} }

View File

@ -10,13 +10,7 @@ import net.corda.core.messaging.startFlow
import net.corda.core.node.services.queryBy import net.corda.core.node.services.queryBy
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.core.utilities.getOrThrow import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.seconds import net.corda.core.utilities.minutes
import net.corda.coretests.flows.AbstractFlowExternalOperationTest.CustomTableEntity
import net.corda.coretests.flows.AbstractFlowExternalOperationTest.DirectlyAccessedServiceHubException
import net.corda.coretests.flows.AbstractFlowExternalOperationTest.ExternalOperation
import net.corda.coretests.flows.AbstractFlowExternalOperationTest.FlowWithExternalProcess
import net.corda.coretests.flows.AbstractFlowExternalOperationTest.FutureService
import net.corda.coretests.flows.AbstractFlowExternalOperationTest.MyCordaException
import net.corda.testing.contracts.DummyContract import net.corda.testing.contracts.DummyContract
import net.corda.testing.contracts.DummyState import net.corda.testing.contracts.DummyState
import net.corda.testing.core.ALICE_NAME import net.corda.testing.core.ALICE_NAME
@ -26,29 +20,24 @@ import net.corda.testing.driver.DriverParameters
import net.corda.testing.driver.driver import net.corda.testing.driver.driver
import net.corda.testing.node.internal.cordappsForPackages import net.corda.testing.node.internal.cordappsForPackages
import org.junit.Test import org.junit.Test
import java.lang.IllegalStateException
import java.sql.SQLTransientConnectionException import java.sql.SQLTransientConnectionException
import java.util.concurrent.TimeoutException
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith import kotlin.test.assertFailsWith
import kotlin.test.assertTrue import kotlin.test.assertTrue
class FlowExternalOperationTest : AbstractFlowExternalOperationTest() { class FlowExternalOperationTest : AbstractFlowExternalOperationTest() {
@Test(timeout=300_000) @Test(timeout = 300_000)
fun `external operation`() { fun `external operation`() {
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) { driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
val alice = startNode(providedName = ALICE_NAME).getOrThrow() val alice = startNode(providedName = ALICE_NAME).getOrThrow()
val bob = startNode(providedName = BOB_NAME).getOrThrow() val bob = startNode(providedName = BOB_NAME).getOrThrow()
alice.rpc.startFlow(::FlowWithExternalOperation, bob.nodeInfo.singleIdentity()) alice.rpc.startFlow(::FlowWithExternalOperation, bob.nodeInfo.singleIdentity())
.returnValue.getOrThrow(20.seconds) .returnValue.getOrThrow(1.minutes)
val (discharged, observation) = alice.rpc.startFlow(::GetHospitalCountersFlow).returnValue.getOrThrow() assertHospitalCounters(0, 0)
assertEquals(0, discharged)
assertEquals(0, observation)
} }
} }
@Test(timeout=300_000) @Test(timeout = 300_000)
fun `external operation that checks deduplicationId is not rerun when flow is retried`() { fun `external operation that checks deduplicationId is not rerun when flow is retried`() {
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) { driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
val alice = startNode(providedName = ALICE_NAME).getOrThrow() val alice = startNode(providedName = ALICE_NAME).getOrThrow()
@ -57,15 +46,13 @@ class FlowExternalOperationTest : AbstractFlowExternalOperationTest() {
alice.rpc.startFlow( alice.rpc.startFlow(
::FlowWithExternalOperationWithDeduplication, ::FlowWithExternalOperationWithDeduplication,
bob.nodeInfo.singleIdentity() bob.nodeInfo.singleIdentity()
).returnValue.getOrThrow(20.seconds) ).returnValue.getOrThrow(1.minutes)
} }
val (discharged, observation) = alice.rpc.startFlow(::GetHospitalCountersFlow).returnValue.getOrThrow() assertHospitalCounters(1, 0)
assertEquals(1, discharged)
assertEquals(0, observation)
} }
} }
@Test(timeout=300_000) @Test(timeout = 300_000)
fun `external operation propagates exception to calling flow`() { fun `external operation propagates exception to calling flow`() {
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) { driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
val alice = startNode(providedName = ALICE_NAME).getOrThrow() val alice = startNode(providedName = ALICE_NAME).getOrThrow()
@ -75,81 +62,71 @@ class FlowExternalOperationTest : AbstractFlowExternalOperationTest() {
::FlowWithExternalOperationPropagatesException, ::FlowWithExternalOperationPropagatesException,
bob.nodeInfo.singleIdentity(), bob.nodeInfo.singleIdentity(),
MyCordaException::class.java MyCordaException::class.java
).returnValue.getOrThrow(20.seconds) ).returnValue.getOrThrow(1.minutes)
} }
val (discharged, observation) = alice.rpc.startFlow(::GetHospitalCountersFlow).returnValue.getOrThrow() assertHospitalCounters(0, 0)
assertEquals(0, discharged)
assertEquals(0, observation)
} }
} }
@Test(timeout=300_000) @Test(timeout = 300_000)
fun `external operation exception can be caught in flow`() { fun `external operation exception can be caught in flow`() {
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) { driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
val alice = startNode(providedName = ALICE_NAME).getOrThrow() val alice = startNode(providedName = ALICE_NAME).getOrThrow()
val bob = startNode(providedName = BOB_NAME).getOrThrow() val bob = startNode(providedName = BOB_NAME).getOrThrow()
alice.rpc.startFlow(::FlowWithExternalOperationThatThrowsExceptionAndCaughtInFlow, bob.nodeInfo.singleIdentity()) alice.rpc.startFlow(::FlowWithExternalOperationThatThrowsExceptionAndCaughtInFlow, bob.nodeInfo.singleIdentity())
.returnValue.getOrThrow(20.seconds) .returnValue.getOrThrow(1.minutes)
val (discharged, observation) = alice.rpc.startFlow(::GetHospitalCountersFlow).returnValue.getOrThrow() assertHospitalCounters(0, 0)
assertEquals(0, discharged)
assertEquals(0, observation)
} }
} }
@Test(timeout=300_000) @Test(timeout = 300_000)
fun `external operation with exception that hospital keeps for observation does not fail`() { fun `external operation with exception that hospital keeps for observation does not fail`() {
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) { driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
val alice = startNode(providedName = ALICE_NAME).getOrThrow() val alice = startNode(providedName = ALICE_NAME).getOrThrow()
val bob = startNode(providedName = BOB_NAME).getOrThrow() val bob = startNode(providedName = BOB_NAME).getOrThrow()
assertFailsWith<TimeoutException> { blockUntilFlowKeptInForObservation {
alice.rpc.startFlow( alice.rpc.startFlow(
::FlowWithExternalOperationPropagatesException, ::FlowWithExternalOperationPropagatesException,
bob.nodeInfo.singleIdentity(), bob.nodeInfo.singleIdentity(),
HospitalizeFlowException::class.java HospitalizeFlowException::class.java
).returnValue.getOrThrow(20.seconds) )
} }
val (discharged, observation) = alice.rpc.startFlow(::GetHospitalCountersFlow).returnValue.getOrThrow() assertHospitalCounters(0, 1)
assertEquals(0, discharged)
assertEquals(1, observation)
} }
} }
@Test(timeout=300_000) @Test(timeout = 300_000)
fun `external operation with exception that hospital discharges is retried and runs the external operation again`() { fun `external operation with exception that hospital discharges is retried and runs the external operation again`() {
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) { driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
val alice = startNode(providedName = ALICE_NAME).getOrThrow() val alice = startNode(providedName = ALICE_NAME).getOrThrow()
val bob = startNode(providedName = BOB_NAME).getOrThrow() val bob = startNode(providedName = BOB_NAME).getOrThrow()
assertFailsWith<TimeoutException> { blockUntilFlowKeptInForObservation {
alice.rpc.startFlow( alice.rpc.startFlow(
::FlowWithExternalOperationPropagatesException, ::FlowWithExternalOperationPropagatesException,
bob.nodeInfo.singleIdentity(), bob.nodeInfo.singleIdentity(),
SQLTransientConnectionException::class.java SQLTransientConnectionException::class.java
).returnValue.getOrThrow(20.seconds) )
} }
val (discharged, observation) = alice.rpc.startFlow(::GetHospitalCountersFlow).returnValue.getOrThrow() assertHospitalCounters(3, 1)
assertEquals(3, discharged)
assertEquals(1, observation)
} }
} }
@Test(timeout=300_000) @Test(timeout = 300_000)
fun `external async operation that passes serviceHub into process can be retried`() { fun `external async operation that passes serviceHub into process can be retried`() {
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) { driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
val alice = startNode(providedName = ALICE_NAME).getOrThrow() val alice = startNode(providedName = ALICE_NAME).getOrThrow()
val bob = startNode(providedName = BOB_NAME).getOrThrow() val bob = startNode(providedName = BOB_NAME).getOrThrow()
assertFailsWith<TimeoutException> { blockUntilFlowKeptInForObservation {
alice.rpc.startFlow( alice.rpc.startFlow(
::FlowWithExternalOperationThatPassesInServiceHubCanRetry, ::FlowWithExternalOperationThatPassesInServiceHubCanRetry,
bob.nodeInfo.singleIdentity() bob.nodeInfo.singleIdentity()
).returnValue.getOrThrow(20.seconds) )
} }
val (discharged, observation) = alice.rpc.startFlow(::GetHospitalCountersFlow).returnValue.getOrThrow() assertHospitalCounters(3, 1)
assertEquals(3, discharged)
assertEquals(1, observation)
} }
} }
@Test(timeout=300_000) @Test(timeout = 300_000)
fun `external async operation that accesses serviceHub from flow directly will fail when retried`() { fun `external async operation that accesses serviceHub from flow directly will fail when retried`() {
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) { driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
val alice = startNode(providedName = ALICE_NAME).getOrThrow() val alice = startNode(providedName = ALICE_NAME).getOrThrow()
@ -158,15 +135,13 @@ class FlowExternalOperationTest : AbstractFlowExternalOperationTest() {
alice.rpc.startFlow( alice.rpc.startFlow(
::FlowWithExternalOperationThatDirectlyAccessesServiceHubFailsRetry, ::FlowWithExternalOperationThatDirectlyAccessesServiceHubFailsRetry,
bob.nodeInfo.singleIdentity() bob.nodeInfo.singleIdentity()
).returnValue.getOrThrow(20.seconds) ).returnValue.getOrThrow(1.minutes)
} }
val (discharged, observation) = alice.rpc.startFlow(::GetHospitalCountersFlow).returnValue.getOrThrow() assertHospitalCounters(1, 0)
assertEquals(1, discharged)
assertEquals(0, observation)
} }
} }
@Test(timeout=300_000) @Test(timeout = 300_000)
fun `vault can be queried`() { fun `vault can be queried`() {
driver( driver(
DriverParameters( DriverParameters(
@ -176,52 +151,52 @@ class FlowExternalOperationTest : AbstractFlowExternalOperationTest() {
) { ) {
val alice = startNode(providedName = ALICE_NAME).getOrThrow() val alice = startNode(providedName = ALICE_NAME).getOrThrow()
val success = alice.rpc.startFlow(::FlowWithWithExternalOperationThatQueriesVault) val success = alice.rpc.startFlow(::FlowWithWithExternalOperationThatQueriesVault)
.returnValue.getOrThrow(20.seconds) .returnValue.getOrThrow(1.minutes)
assertTrue(success) assertTrue(success)
} }
} }
@Test(timeout=300_000) @Test(timeout = 300_000)
fun `data can be persisted to node database via entity manager`() { fun `data can be persisted to node database via entity manager`() {
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) { driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
val alice = startNode(providedName = ALICE_NAME).getOrThrow() val alice = startNode(providedName = ALICE_NAME).getOrThrow()
val success = alice.rpc.startFlow(::FlowWithExternalOperationThatPersistsViaEntityManager) val success = alice.rpc.startFlow(::FlowWithExternalOperationThatPersistsViaEntityManager)
.returnValue.getOrThrow(20.seconds) .returnValue.getOrThrow(1.minutes)
assertTrue(success) assertTrue(success)
} }
} }
@Test(timeout=300_000) @Test(timeout = 300_000)
fun `data can be persisted to node database via jdbc session`() { fun `data can be persisted to node database via jdbc session`() {
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) { driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
val alice = startNode(providedName = ALICE_NAME).getOrThrow() val alice = startNode(providedName = ALICE_NAME).getOrThrow()
val success = alice.rpc.startFlow(::FlowWithExternalOperationThatPersistsViaJdbcSession) val success = alice.rpc.startFlow(::FlowWithExternalOperationThatPersistsViaJdbcSession)
.returnValue.getOrThrow(20.seconds) .returnValue.getOrThrow(1.minutes)
assertTrue(success) assertTrue(success)
} }
} }
@Test(timeout=300_000) @Test(timeout = 300_000)
fun `data can be persisted to node database via servicehub database transaction`() { fun `data can be persisted to node database via servicehub database transaction`() {
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) { driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
val alice = startNode(providedName = ALICE_NAME).getOrThrow() val alice = startNode(providedName = ALICE_NAME).getOrThrow()
val success = alice.rpc.startFlow(::FlowWithExternalOperationThatPersistsViaDatabaseTransaction) val success = alice.rpc.startFlow(::FlowWithExternalOperationThatPersistsViaDatabaseTransaction)
.returnValue.getOrThrow(20.seconds) .returnValue.getOrThrow(1.minutes)
assertTrue(success) assertTrue(success)
} }
} }
@Test(timeout=300_000) @Test(timeout = 300_000)
fun `data can be persisted to node database in external operation and read from another process once finished`() { fun `data can be persisted to node database in external operation and read from another process once finished`() {
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) { driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
val alice = startNode(providedName = ALICE_NAME).getOrThrow() val alice = startNode(providedName = ALICE_NAME).getOrThrow()
val success = alice.rpc.startFlow(::FlowWithExternalOperationThatPersistsToDatabaseAndReadsFromExternalOperation) val success = alice.rpc.startFlow(::FlowWithExternalOperationThatPersistsToDatabaseAndReadsFromExternalOperation)
.returnValue.getOrThrow(20.seconds) .returnValue.getOrThrow(1.minutes)
assertTrue(success) assertTrue(success)
} }
} }
@Test(timeout=300_000) @Test(timeout = 300_000)
fun `external operation can be retried when an error occurs inside of database transaction`() { fun `external operation can be retried when an error occurs inside of database transaction`() {
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) { driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
val alice = startNode(providedName = ALICE_NAME).getOrThrow() val alice = startNode(providedName = ALICE_NAME).getOrThrow()
@ -229,11 +204,9 @@ class FlowExternalOperationTest : AbstractFlowExternalOperationTest() {
val success = alice.rpc.startFlow( val success = alice.rpc.startFlow(
::FlowWithExternalOperationThatErrorsInsideOfDatabaseTransaction, ::FlowWithExternalOperationThatErrorsInsideOfDatabaseTransaction,
bob.nodeInfo.singleIdentity() bob.nodeInfo.singleIdentity()
).returnValue.getOrThrow(20.seconds) ).returnValue.getOrThrow(1.minutes)
assertTrue(success as Boolean) assertTrue(success as Boolean)
val (discharged, observation) = alice.rpc.startFlow(::GetHospitalCountersFlow).returnValue.getOrThrow() assertHospitalCounters(1, 0)
assertEquals(1, discharged)
assertEquals(0, observation)
} }
} }
@ -260,7 +233,10 @@ class FlowExternalOperationTest : AbstractFlowExternalOperationTest() {
FlowWithExternalProcess(party) { FlowWithExternalProcess(party) {
@Suspendable @Suspendable
override fun testCode(): Any = await(ExternalOperation(serviceHub) { _, _ -> throw createException() }) override fun testCode() {
val e = createException()
await(ExternalOperation(serviceHub) { _, _ -> throw e })
}
private fun createException() = when (exceptionType) { private fun createException() = when (exceptionType) {
HospitalizeFlowException::class.java -> HospitalizeFlowException("keep it around") HospitalizeFlowException::class.java -> HospitalizeFlowException("keep it around")

View File

@ -0,0 +1,364 @@
package net.corda.coretests.flows
import co.paralleluniverse.fibers.Suspendable
import net.corda.core.flows.FlowLogic
import net.corda.core.flows.FlowSession
import net.corda.core.flows.InitiatedBy
import net.corda.core.flows.InitiatingFlow
import net.corda.core.flows.KilledFlowException
import net.corda.core.flows.StartableByRPC
import net.corda.core.flows.StateMachineRunId
import net.corda.core.flows.UnexpectedFlowEndException
import net.corda.core.identity.Party
import net.corda.core.messaging.startFlow
import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.minutes
import net.corda.core.utilities.seconds
import net.corda.testing.core.ALICE_NAME
import net.corda.testing.core.BOB_NAME
import net.corda.testing.core.CHARLIE_NAME
import net.corda.testing.core.singleIdentity
import net.corda.testing.driver.DriverParameters
import net.corda.testing.driver.driver
import org.apache.logging.log4j.Level
import org.apache.logging.log4j.core.config.Configurator
import org.assertj.core.api.Assertions.assertThatExceptionOfType
import org.junit.Before
import org.junit.Test
import java.util.concurrent.Semaphore
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertTrue
class FlowIsKilledTest {
private companion object {
const val EXCEPTION_MESSAGE = "Goodbye, cruel world!"
}
@Before
fun setup() {
Configurator.setLevel("net.corda.node.services.statemachine", Level.DEBUG)
}
@Test(timeout = 300_000)
fun `manually handle the isKilled check`() {
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
val alice = startNode(providedName = ALICE_NAME).getOrThrow()
alice.rpc.let { rpc ->
val handle = rpc.startFlow(::AFlowThatWantsToDie)
AFlowThatWantsToDie.lockA.acquire()
rpc.killFlow(handle.id)
AFlowThatWantsToDie.lockB.release()
assertThatExceptionOfType(KilledFlowException::class.java)
.isThrownBy { handle.returnValue.getOrThrow(1.minutes) }
.withMessage(EXCEPTION_MESSAGE)
assertEquals(11, AFlowThatWantsToDie.position)
val checkpoints = rpc.startFlow(::GetNumberOfCheckpointsFlow).returnValue.getOrThrow(20.seconds)
assertEquals(1, checkpoints)
}
}
}
@Test(timeout = 300_000)
fun `manually handled killed flows propagate error to counter parties`() {
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
val alice = startNode(providedName = ALICE_NAME).getOrThrow()
val bob = startNode(providedName = BOB_NAME).getOrThrow()
val charlie = startNode(providedName = CHARLIE_NAME).getOrThrow()
alice.rpc.let { rpc ->
val handle = rpc.startFlow(
::AFlowThatWantsToDieAndKillsItsFriends,
listOf(bob.nodeInfo.singleIdentity(), charlie.nodeInfo.singleIdentity())
)
AFlowThatWantsToDieAndKillsItsFriends.lockA.acquire()
AFlowThatWantsToDieAndKillsItsFriendsResponder.locks.forEach { it.value.acquire() }
rpc.killFlow(handle.id)
AFlowThatWantsToDieAndKillsItsFriends.lockB.release()
assertThatExceptionOfType(KilledFlowException::class.java)
.isThrownBy { handle.returnValue.getOrThrow(1.minutes) }
.withMessage(EXCEPTION_MESSAGE)
AFlowThatWantsToDieAndKillsItsFriendsResponder.locks.forEach { it.value.acquire() }
assertEquals(11, AFlowThatWantsToDieAndKillsItsFriends.position)
assertTrue(AFlowThatWantsToDieAndKillsItsFriendsResponder.receivedKilledExceptions[BOB_NAME]!!)
assertTrue(AFlowThatWantsToDieAndKillsItsFriendsResponder.receivedKilledExceptions[CHARLIE_NAME]!!)
val aliceCheckpoints = alice.rpc.startFlow(::GetNumberOfCheckpointsFlow).returnValue.getOrThrow(20.seconds)
assertEquals(1, aliceCheckpoints)
val bobCheckpoints = bob.rpc.startFlow(::GetNumberOfCheckpointsFlow).returnValue.getOrThrow(20.seconds)
assertEquals(1, bobCheckpoints)
}
}
}
@Test(timeout = 300_000)
fun `a manually killed initiated flow will propagate the killed error to the initiator and its counter parties`() {
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
val alice = startNode(providedName = ALICE_NAME).getOrThrow()
val bob = startNode(providedName = BOB_NAME).getOrThrow()
val handle = alice.rpc.startFlow(
::AFlowThatGetsMurderedByItsFriend,
bob.nodeInfo.singleIdentity()
)
AFlowThatGetsMurderedByItsFriendResponder.lockA.acquire()
val initiatedFlowId = AFlowThatGetsMurderedByItsFriendResponder.flowId!!
bob.rpc.killFlow(initiatedFlowId)
AFlowThatGetsMurderedByItsFriendResponder.lockB.release()
assertFailsWith<UnexpectedFlowEndException> {
handle.returnValue.getOrThrow(1.minutes)
}
assertTrue(AFlowThatGetsMurderedByItsFriend.receivedKilledException)
assertEquals(11, AFlowThatGetsMurderedByItsFriendResponder.position)
val aliceCheckpoints = alice.rpc.startFlow(::GetNumberOfCheckpointsFlow).returnValue.getOrThrow(20.seconds)
assertEquals(1, aliceCheckpoints)
val bobCheckpoints = bob.rpc.startFlow(::GetNumberOfCheckpointsFlow).returnValue.getOrThrow(20.seconds)
assertEquals(1, bobCheckpoints)
}
}
@Test(timeout = 300_000)
fun `manually handle killed flows using checkForIsNotKilled`() {
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
val alice = startNode(providedName = ALICE_NAME).getOrThrow()
alice.rpc.let { rpc ->
val handle = rpc.startFlow(::AFlowThatChecksIfItWantsToDie)
AFlowThatChecksIfItWantsToDie.lockA.acquire()
rpc.killFlow(handle.id)
AFlowThatChecksIfItWantsToDie.lockB.release()
assertThatExceptionOfType(KilledFlowException::class.java)
.isThrownBy { handle.returnValue.getOrThrow(1.minutes) }
.withMessageNotContaining(EXCEPTION_MESSAGE)
assertEquals(11, AFlowThatChecksIfItWantsToDie.position)
val checkpoints = rpc.startFlow(::GetNumberOfCheckpointsFlow).returnValue.getOrThrow(20.seconds)
assertEquals(1, checkpoints)
}
}
}
@Test(timeout = 300_000)
fun `manually handle killed flows using checkForIsNotKilled with lazy message`() {
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
val alice = startNode(providedName = ALICE_NAME).getOrThrow()
alice.rpc.let { rpc ->
val handle = rpc.startFlow(::AFlowThatChecksIfItWantsToDieAndLeavesANote)
AFlowThatChecksIfItWantsToDieAndLeavesANote.lockA.acquire()
rpc.killFlow(handle.id)
AFlowThatChecksIfItWantsToDieAndLeavesANote.lockB.release()
assertThatExceptionOfType(KilledFlowException::class.java)
.isThrownBy { handle.returnValue.getOrThrow(1.minutes) }
.withMessage(EXCEPTION_MESSAGE)
assertEquals(11, AFlowThatChecksIfItWantsToDie.position)
val checkpoints = rpc.startFlow(::GetNumberOfCheckpointsFlow).returnValue.getOrThrow(20.seconds)
assertEquals(1, checkpoints)
}
}
}
@StartableByRPC
class AFlowThatWantsToDie : FlowLogic<Unit>() {
companion object {
val lockA = Semaphore(0)
val lockB = Semaphore(0)
var position = 0
}
@Suspendable
override fun call() {
for (i in 0..100) {
position = i
logger.info("i = $i")
if (isKilled) {
throw KilledFlowException(runId, EXCEPTION_MESSAGE)
}
if (i == 10) {
lockA.release()
lockB.acquire()
}
}
}
}
@StartableByRPC
@InitiatingFlow
class AFlowThatWantsToDieAndKillsItsFriends(private val parties: List<Party>) : FlowLogic<Unit>() {
companion object {
val lockA = Semaphore(0)
val lockB = Semaphore(0)
var position = 0
}
@Suspendable
override fun call() {
val sessionOne = initiateFlow(parties[0])
val sessionTwo = initiateFlow(parties[1])
// trigger sessions with 2 counter parties
sessionOne.sendAndReceive<String>("what is up")
sessionOne.send("what is up 2")
sessionTwo.sendAndReceive<String>("what is up")
sessionTwo.send("what is up 2")
for (i in 0..100) {
position = i
logger.info("i = $i")
if (isKilled) {
throw KilledFlowException(runId, EXCEPTION_MESSAGE)
}
if (i == 10) {
lockA.release()
lockB.acquire()
}
}
}
}
@InitiatedBy(AFlowThatWantsToDieAndKillsItsFriends::class)
class AFlowThatWantsToDieAndKillsItsFriendsResponder(private val session: FlowSession) : FlowLogic<Unit>() {
companion object {
val locks = mapOf(
BOB_NAME to Semaphore(0),
CHARLIE_NAME to Semaphore(0)
)
var receivedKilledExceptions = mutableMapOf(
BOB_NAME to false,
CHARLIE_NAME to false
)
}
@Suspendable
override fun call() {
session.receive<String>()
session.send("hi")
session.receive<String>()
locks[ourIdentity.name]!!.release()
try {
session.receive<String>()
} catch (e: UnexpectedFlowEndException) {
receivedKilledExceptions[ourIdentity.name] = true
locks[ourIdentity.name]!!.release()
throw e
}
}
}
@StartableByRPC
@InitiatingFlow
class AFlowThatGetsMurderedByItsFriend(private val party: Party) : FlowLogic<Unit>() {
companion object {
var receivedKilledException = false
}
@Suspendable
override fun call() {
val sessionOne = initiateFlow(party)
// trigger sessions with 2 counter parties
sessionOne.sendAndReceive<String>("what is up")
try {
sessionOne.receive<String>()
} catch (e: UnexpectedFlowEndException) {
receivedKilledException = true
throw e
}
}
}
@InitiatedBy(AFlowThatGetsMurderedByItsFriend::class)
class AFlowThatGetsMurderedByItsFriendResponder(private val session: FlowSession) : FlowLogic<Unit>() {
companion object {
val lockA = Semaphore(0)
val lockB = Semaphore(0)
var flowId: StateMachineRunId? = null
var position = 0
}
@Suspendable
override fun call() {
flowId = runId
session.receive<String>()
session.send("hi")
for (i in 0..100) {
position = i
if (isKilled) {
throw KilledFlowException(runId, EXCEPTION_MESSAGE)
}
if (i == 10) {
lockA.release()
lockB.acquire()
}
}
}
}
@StartableByRPC
class AFlowThatChecksIfItWantsToDie : FlowLogic<Unit>() {
companion object {
val lockA = Semaphore(0)
val lockB = Semaphore(0)
var position = 0
}
@Suspendable
override fun call() {
for (i in 0..100) {
position = i
logger.info("i = $i")
checkFlowIsNotKilled()
if (i == 10) {
lockA.release()
lockB.acquire()
}
}
}
}
@StartableByRPC
class AFlowThatChecksIfItWantsToDieAndLeavesANote : FlowLogic<Unit>() {
companion object {
val lockA = Semaphore(0)
val lockB = Semaphore(0)
var position = 0
}
@Suspendable
override fun call() {
for (i in 0..100) {
position = i
logger.info("i = $i")
checkFlowIsNotKilled { EXCEPTION_MESSAGE }
if (i == 10) {
lockA.release()
lockB.acquire()
}
}
}
}
@StartableByRPC
class GetNumberOfCheckpointsFlow : FlowLogic<Long>() {
override fun call(): Long {
return serviceHub.jdbcSession().prepareStatement("select count(*) from node_checkpoints").use { ps ->
ps.executeQuery().use { rs ->
rs.next()
rs.getLong(1)
}
}
}
}
}

View File

@ -0,0 +1,146 @@
package net.corda.coretests.flows
import co.paralleluniverse.fibers.Suspendable
import net.corda.core.flows.FlowLogic
import net.corda.core.flows.FlowSession
import net.corda.core.flows.InitiatedBy
import net.corda.core.flows.InitiatingFlow
import net.corda.core.flows.StartableByRPC
import net.corda.core.identity.Party
import net.corda.core.messaging.startFlow
import net.corda.core.utilities.getOrThrow
import net.corda.core.utilities.minutes
import net.corda.core.utilities.seconds
import net.corda.core.utilities.unwrap
import net.corda.testing.core.ALICE_NAME
import net.corda.testing.core.BOB_NAME
import net.corda.testing.core.singleIdentity
import net.corda.testing.driver.DriverParameters
import net.corda.testing.driver.driver
import org.apache.logging.log4j.Level
import org.apache.logging.log4j.core.config.Configurator
import org.junit.Before
import org.junit.Test
import java.time.Duration
import java.time.Instant
import kotlin.test.assertTrue
class FlowSleepTest {
@Before
fun setup() {
Configurator.setLevel("net.corda.node.services.statemachine", Level.DEBUG)
}
@Test(timeout = 300_000)
fun `flow can sleep`() {
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
val alice = startNode(providedName = ALICE_NAME).getOrThrow()
val (start, finish) = alice.rpc.startFlow(::SleepyFlow).returnValue.getOrThrow(1.minutes)
val difference = Duration.between(start, finish)
assertTrue(difference >= 5.seconds)
assertTrue(difference < 7.seconds)
}
}
@Test(timeout = 300_000)
fun `flow can sleep multiple times`() {
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
val alice = startNode(providedName = ALICE_NAME).getOrThrow()
val (start, middle, finish) = alice.rpc.startFlow(::AnotherSleepyFlow).returnValue.getOrThrow(1.minutes)
val differenceBetweenStartAndMiddle = Duration.between(start, middle)
val differenceBetweenMiddleAndFinish = Duration.between(middle, finish)
assertTrue(differenceBetweenStartAndMiddle >= 5.seconds)
assertTrue(differenceBetweenStartAndMiddle < 7.seconds)
assertTrue(differenceBetweenMiddleAndFinish >= 10.seconds)
assertTrue(differenceBetweenMiddleAndFinish < 12.seconds)
}
}
@Test(timeout = 300_000)
fun `flow can sleep and perform other suspending functions`() {
// ensures that events received while the flow is sleeping are not processed
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
val alice = startNode(providedName = ALICE_NAME).getOrThrow()
val bob = startNode(providedName = BOB_NAME).getOrThrow()
val (start, finish) = alice.rpc.startFlow(
::SleepAndInteractWithPartyFlow,
bob.nodeInfo.singleIdentity()
).returnValue.getOrThrow(1.minutes)
val difference = Duration.between(start, finish)
assertTrue(difference >= 5.seconds)
assertTrue(difference < 7.seconds)
}
}
@StartableByRPC
class SleepyFlow : FlowLogic<Pair<Instant, Instant>>() {
@Suspendable
override fun call(): Pair<Instant, Instant> {
val start = Instant.now()
sleep(5.seconds)
return start to Instant.now()
}
}
@StartableByRPC
class AnotherSleepyFlow : FlowLogic<Triple<Instant, Instant, Instant>>() {
@Suspendable
override fun call(): Triple<Instant, Instant, Instant> {
val start = Instant.now()
sleep(5.seconds)
val middle = Instant.now()
sleep(10.seconds)
return Triple(start, middle, Instant.now())
}
}
@StartableByRPC
@InitiatingFlow
class SleepAndInteractWithPartyFlow(private val party: Party) : FlowLogic<Pair<Instant, Instant>>() {
@Suspendable
override fun call(): Pair<Instant, Instant> {
subFlow(PingPongFlow(party))
val start = Instant.now()
sleep(5.seconds)
val finish = Instant.now()
val session = initiateFlow(party)
session.sendAndReceive<String>("hi")
session.sendAndReceive<String>("hi")
subFlow(PingPongFlow(party))
return start to finish
}
}
@InitiatedBy(SleepAndInteractWithPartyFlow::class)
class SleepAndInteractWithPartyResponder(val session: FlowSession) : FlowLogic<Unit>() {
@Suspendable
override fun call() {
session.receive<String>().unwrap { it }
session.send("go away")
session.receive<String>().unwrap { it }
session.send("go away")
}
}
@InitiatingFlow
class PingPongFlow(val party: Party) : FlowLogic<Unit>() {
@Suspendable
override fun call() {
val session = initiateFlow(party)
session.sendAndReceive<String>("ping pong").unwrap { it }
}
}
@InitiatedBy(PingPongFlow::class)
class PingPongResponder(val session: FlowSession) : FlowLogic<Unit>() {
@Suspendable
override fun call() {
session.receive<String>().unwrap { it }
session.send("I got you bro")
}
}
}

View File

@ -21,14 +21,18 @@ import net.corda.testing.internal.services.InternalMockAttachmentStorage
import net.corda.testing.node.internal.FINANCE_CONTRACTS_CORDAPP import net.corda.testing.node.internal.FINANCE_CONTRACTS_CORDAPP
import net.corda.testing.services.MockAttachmentStorage import net.corda.testing.services.MockAttachmentStorage
import org.apache.commons.io.IOUtils import org.apache.commons.io.IOUtils
import org.assertj.core.api.Assertions.assertThat
import org.junit.Assert.assertArrayEquals import org.junit.Assert.assertArrayEquals
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.io.InputStream import java.io.InputStream
import java.net.URL import java.net.URL
import kotlin.test.assertFailsWith import kotlin.test.assertFailsWith
import kotlin.test.fail
class AttachmentsClassLoaderTests { class AttachmentsClassLoaderTests {
companion object { companion object {
@ -84,15 +88,30 @@ class AttachmentsClassLoaderTests {
} }
} }
@Test(timeout=300_000)
fun `test contracts have no permissions for protection domain`() {
val isolatedId = importAttachment(ISOLATED_CONTRACTS_JAR_PATH.openStream(), "app", "isolated.jar")
assertNull(System.getSecurityManager())
createClassloader(isolatedId).use { classLoader ->
val contractClass = Class.forName(ISOLATED_CONTRACT_CLASS_NAME, true, classLoader)
val protectionDomain = contractClass.protectionDomain ?: fail("Protection Domain missing")
val permissions = protectionDomain.permissions ?: fail("Protection domain has no permissions")
assertThat(permissions.elements().toList()).isEmpty()
assertTrue(permissions.isReadOnly)
}
}
@Test(timeout=300_000) @Test(timeout=300_000)
fun `Dynamically load AnotherDummyContract from isolated contracts jar using the AttachmentsClassLoader`() { fun `Dynamically load AnotherDummyContract from isolated contracts jar using the AttachmentsClassLoader`() {
val isolatedId = importAttachment(ISOLATED_CONTRACTS_JAR_PATH.openStream(), "app", "isolated.jar") val isolatedId = importAttachment(ISOLATED_CONTRACTS_JAR_PATH.openStream(), "app", "isolated.jar")
val classloader = createClassloader(isolatedId) createClassloader(isolatedId).use { classloader ->
val contractClass = Class.forName(ISOLATED_CONTRACT_CLASS_NAME, true, classloader) val contractClass = Class.forName(ISOLATED_CONTRACT_CLASS_NAME, true, classloader)
val contract = contractClass.getDeclaredConstructor().newInstance() as Contract val contract = contractClass.getDeclaredConstructor().newInstance() as Contract
assertEquals("helloworld", contract.declaredField<Any?>("magicString").value) assertEquals("helloworld", contract.declaredField<Any?>("magicString").value)
} }
}
@Test(timeout=300_000) @Test(timeout=300_000)
fun `Test non-overlapping contract jar`() { fun `Test non-overlapping contract jar`() {
@ -100,7 +119,7 @@ class AttachmentsClassLoaderTests {
val att2 = importAttachment(ISOLATED_CONTRACTS_JAR_PATH_V4.openStream(), "app", "isolated-4.0.jar") val att2 = importAttachment(ISOLATED_CONTRACTS_JAR_PATH_V4.openStream(), "app", "isolated-4.0.jar")
assertFailsWith(TransactionVerificationException.OverlappingAttachmentsException::class) { assertFailsWith(TransactionVerificationException.OverlappingAttachmentsException::class) {
createClassloader(listOf(att1, att2)) createClassloader(listOf(att1, att2)).use {}
} }
} }
@ -111,7 +130,7 @@ class AttachmentsClassLoaderTests {
val isolatedSignedId = importAttachment(signedJar.first.toUri().toURL().openStream(), "app", "isolated-signed.jar") val isolatedSignedId = importAttachment(signedJar.first.toUri().toURL().openStream(), "app", "isolated-signed.jar")
// does not throw OverlappingAttachments exception // does not throw OverlappingAttachments exception
createClassloader(listOf(isolatedId, isolatedSignedId)) createClassloader(listOf(isolatedId, isolatedSignedId)).use {}
} }
@Test(timeout=300_000) @Test(timeout=300_000)
@ -120,7 +139,7 @@ class AttachmentsClassLoaderTests {
val att2 = importAttachment(FINANCE_CONTRACTS_CORDAPP.jarFile.inputStream(), "app", "finance.jar") val att2 = importAttachment(FINANCE_CONTRACTS_CORDAPP.jarFile.inputStream(), "app", "finance.jar")
// does not throw OverlappingAttachments exception // does not throw OverlappingAttachments exception
createClassloader(listOf(att1, att2)) createClassloader(listOf(att1, att2)).use {}
} }
@Test(timeout=300_000) @Test(timeout=300_000)
@ -128,23 +147,25 @@ class AttachmentsClassLoaderTests {
val att1 = importAttachment(fakeAttachment("file1.txt", "some data").inputStream(), "app", "file1.jar") val att1 = importAttachment(fakeAttachment("file1.txt", "some data").inputStream(), "app", "file1.jar")
val att2 = importAttachment(fakeAttachment("file2.txt", "some other data").inputStream(), "app", "file2.jar") val att2 = importAttachment(fakeAttachment("file2.txt", "some other data").inputStream(), "app", "file2.jar")
val cl = createClassloader(listOf(att1, att2)) createClassloader(listOf(att1, att2)).use { cl ->
val txt = IOUtils.toString(cl.getResourceAsStream("file1.txt"), Charsets.UTF_8.name()) val txt = IOUtils.toString(cl.getResourceAsStream("file1.txt"), Charsets.UTF_8.name())
assertEquals("some data", txt) assertEquals("some data", txt)
val txt1 = IOUtils.toString(cl.getResourceAsStream("file2.txt"), Charsets.UTF_8.name()) val txt1 = IOUtils.toString(cl.getResourceAsStream("file2.txt"), Charsets.UTF_8.name())
assertEquals("some other data", txt1) assertEquals("some other data", txt1)
} }
}
@Test(timeout=300_000) @Test(timeout=300_000)
fun `Test valid overlapping file condition`() { fun `Test valid overlapping file condition`() {
val att1 = importAttachment(fakeAttachment("file1.txt", "same data", "file2.txt", "same other data").inputStream(), "app", "file1.jar") val att1 = importAttachment(fakeAttachment("file1.txt", "same data", "file2.txt", "same other data").inputStream(), "app", "file1.jar")
val att2 = importAttachment(fakeAttachment("file1.txt", "same data", "file3.txt", "same totally different").inputStream(), "app", "file2.jar") val att2 = importAttachment(fakeAttachment("file1.txt", "same data", "file3.txt", "same totally different").inputStream(), "app", "file2.jar")
val cl = createClassloader(listOf(att1, att2)) createClassloader(listOf(att1, att2)).use { cl ->
val txt = IOUtils.toString(cl.getResourceAsStream("file1.txt"), Charsets.UTF_8.name()) val txt = IOUtils.toString(cl.getResourceAsStream("file1.txt"), Charsets.UTF_8.name())
assertEquals("same data", txt) assertEquals("same data", txt)
} }
}
@Test(timeout=300_000) @Test(timeout=300_000)
fun `No overlapping exception thrown on certain META-INF files`() { fun `No overlapping exception thrown on certain META-INF files`() {
@ -152,7 +173,7 @@ class AttachmentsClassLoaderTests {
val att1 = importAttachment(fakeAttachment(path, "some data").inputStream(), "app", "file1.jar") val att1 = importAttachment(fakeAttachment(path, "some data").inputStream(), "app", "file1.jar")
val att2 = importAttachment(fakeAttachment(path, "some other data").inputStream(), "app", "file2.jar") val att2 = importAttachment(fakeAttachment(path, "some other data").inputStream(), "app", "file2.jar")
createClassloader(listOf(att1, att2)) createClassloader(listOf(att1, att2)).use {}
} }
} }
@ -161,7 +182,7 @@ class AttachmentsClassLoaderTests {
val att1 = importAttachment(fakeAttachment("meta-inf/services/net.corda.core.serialization.SerializationWhitelist", "some data").inputStream(), "app", "file1.jar") val att1 = importAttachment(fakeAttachment("meta-inf/services/net.corda.core.serialization.SerializationWhitelist", "some data").inputStream(), "app", "file1.jar")
val att2 = importAttachment(fakeAttachment("meta-inf/services/net.corda.core.serialization.SerializationWhitelist", "some other data").inputStream(), "app", "file2.jar") val att2 = importAttachment(fakeAttachment("meta-inf/services/net.corda.core.serialization.SerializationWhitelist", "some other data").inputStream(), "app", "file2.jar")
createClassloader(listOf(att1, att2)) createClassloader(listOf(att1, att2)).use {}
} }
@Test(timeout=300_000) @Test(timeout=300_000)
@ -170,7 +191,7 @@ class AttachmentsClassLoaderTests {
val att2 = importAttachment(fakeAttachment("meta-inf/services/com.example.something", "some other data").inputStream(), "app", "file2.jar") val att2 = importAttachment(fakeAttachment("meta-inf/services/com.example.something", "some other data").inputStream(), "app", "file2.jar")
assertFailsWith(TransactionVerificationException.OverlappingAttachmentsException::class) { assertFailsWith(TransactionVerificationException.OverlappingAttachmentsException::class) {
createClassloader(listOf(att1, att2)) createClassloader(listOf(att1, att2)).use {}
} }
} }
@ -180,7 +201,7 @@ class AttachmentsClassLoaderTests {
val att2 = storage.importAttachment(fakeAttachment("file1.txt", "some other data").inputStream(), "app", "file2.jar") val att2 = storage.importAttachment(fakeAttachment("file1.txt", "some other data").inputStream(), "app", "file2.jar")
assertFailsWith(TransactionVerificationException.OverlappingAttachmentsException::class) { assertFailsWith(TransactionVerificationException.OverlappingAttachmentsException::class) {
createClassloader(listOf(att1, att2)) createClassloader(listOf(att1, att2)).use {}
} }
} }
@ -191,7 +212,7 @@ class AttachmentsClassLoaderTests {
val att1 = importAttachment(ISOLATED_CONTRACTS_JAR_PATH.openStream(), "app", ISOLATED_CONTRACTS_JAR_PATH.file) val att1 = importAttachment(ISOLATED_CONTRACTS_JAR_PATH.openStream(), "app", ISOLATED_CONTRACTS_JAR_PATH.file)
val att2 = importAttachment(fakeAttachment("net/corda/finance/contracts/isolated/AnotherDummyContract\$State.class", "some attackdata").inputStream(), "app", "file2.jar") val att2 = importAttachment(fakeAttachment("net/corda/finance/contracts/isolated/AnotherDummyContract\$State.class", "some attackdata").inputStream(), "app", "file2.jar")
assertFailsWith(TransactionVerificationException.OverlappingAttachmentsException::class) { assertFailsWith(TransactionVerificationException.OverlappingAttachmentsException::class) {
createClassloader(listOf(att1, att2)) createClassloader(listOf(att1, att2)).use {}
} }
} }
@ -220,10 +241,10 @@ class AttachmentsClassLoaderTests {
val untrustedClassJar = importAttachment(fakeAttachment("/com/example/something/MaliciousClass.class", "some malicious data").inputStream(), "untrusted", "file2.jar") val untrustedClassJar = importAttachment(fakeAttachment("/com/example/something/MaliciousClass.class", "some malicious data").inputStream(), "untrusted", "file2.jar")
val trustedClassJar = importAttachment(fakeAttachment("/com/example/something/VirtuousClass.class", "some other data").inputStream(), "app", "file3.jar") val trustedClassJar = importAttachment(fakeAttachment("/com/example/something/VirtuousClass.class", "some other data").inputStream(), "app", "file3.jar")
createClassloader(listOf(trustedResourceJar, untrustedResourceJar, trustedClassJar)) createClassloader(listOf(trustedResourceJar, untrustedResourceJar, trustedClassJar)).use {
assertFailsWith(TransactionVerificationException.UntrustedAttachmentsException::class) { assertFailsWith(TransactionVerificationException.UntrustedAttachmentsException::class) {
createClassloader(listOf(trustedResourceJar, untrustedResourceJar, trustedClassJar, untrustedClassJar)) createClassloader(listOf(trustedResourceJar, untrustedResourceJar, trustedClassJar, untrustedClassJar)).use {}
}
} }
} }
@ -257,7 +278,7 @@ class AttachmentsClassLoaderTests {
signers = listOf(keyPairA.public, keyPairB.public) signers = listOf(keyPairA.public, keyPairB.public)
) )
createClassloader(untrustedAttachment) createClassloader(untrustedAttachment).use {}
} }
@Test(timeout=300_000) @Test(timeout=300_000)
@ -287,7 +308,7 @@ class AttachmentsClassLoaderTests {
signers = listOf(keyPairA.public, keyPairB.public) signers = listOf(keyPairA.public, keyPairB.public)
) )
createClassloader(untrustedAttachment) createClassloader(untrustedAttachment).use {}
} }
@Test(timeout=300_000) @Test(timeout=300_000)
@ -306,7 +327,7 @@ class AttachmentsClassLoaderTests {
) )
assertFailsWith(TransactionVerificationException.UntrustedAttachmentsException::class) { assertFailsWith(TransactionVerificationException.UntrustedAttachmentsException::class) {
createClassloader(untrustedAttachment) createClassloader(untrustedAttachment).use {}
} }
} }
@ -337,7 +358,7 @@ class AttachmentsClassLoaderTests {
) )
assertFailsWith(TransactionVerificationException.UntrustedAttachmentsException::class) { assertFailsWith(TransactionVerificationException.UntrustedAttachmentsException::class) {
createClassloader(untrustedAttachment) createClassloader(untrustedAttachment).use {}
} }
} }
@ -380,10 +401,10 @@ class AttachmentsClassLoaderTests {
) )
// pass the inherited trust attachment through the classloader first to ensure it does not affect the next loaded attachment // pass the inherited trust attachment through the classloader first to ensure it does not affect the next loaded attachment
createClassloader(inheritedTrustAttachment) createClassloader(inheritedTrustAttachment).use {
assertFailsWith(TransactionVerificationException.UntrustedAttachmentsException::class) { assertFailsWith(TransactionVerificationException.UntrustedAttachmentsException::class) {
createClassloader(untrustedAttachment) createClassloader(untrustedAttachment).use {}
}
} }
} }
@ -421,7 +442,7 @@ class AttachmentsClassLoaderTests {
) )
assertFailsWith(TransactionVerificationException.UntrustedAttachmentsException::class) { assertFailsWith(TransactionVerificationException.UntrustedAttachmentsException::class) {
createClassloader(untrustedAttachment) createClassloader(untrustedAttachment).use {}
} }
} }
@ -446,6 +467,6 @@ class AttachmentsClassLoaderTests {
signers = listOf(keyPairA.public) signers = listOf(keyPairA.public)
) )
createClassloader(trustedAttachment) createClassloader(trustedAttachment).use {}
} }
} }

View File

@ -101,7 +101,8 @@ task copyQuasarJar(type: Copy) {
jar { jar {
finalizedBy(copyQuasarJar) finalizedBy(copyQuasarJar)
baseName 'corda-core' archiveBaseName = 'corda-core'
archiveClassifier = ''
} }
configurations { configurations {

View File

@ -49,7 +49,7 @@ interface Attachment : NamedByHash {
/** /**
* Finds the named file case insensitively and copies it to the output stream. * Finds the named file case insensitively and copies it to the output stream.
* @throws FileNotFoundException if the given path doesn't exist in the attachment. * @throws [FileNotFoundException] if the given path doesn't exist in the attachment.
*/ */
@JvmDefault @JvmDefault
fun extractFile(path: String, outputTo: OutputStream) = openAsJAR().use { it.extractFile(path, outputTo) } fun extractFile(path: String, outputTo: OutputStream) = openAsJAR().use { it.extractFile(path, outputTo) }

View File

@ -108,7 +108,7 @@ object Crypto {
AlgorithmIdentifier(X9ObjectIdentifiers.ecdsa_with_SHA256, SECObjectIdentifiers.secp256k1), AlgorithmIdentifier(X9ObjectIdentifiers.ecdsa_with_SHA256, SECObjectIdentifiers.secp256k1),
listOf(AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, SECObjectIdentifiers.secp256k1)), listOf(AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, SECObjectIdentifiers.secp256k1)),
cordaBouncyCastleProvider.name, cordaBouncyCastleProvider.name,
"ECDSA", "EC",
"SHA256withECDSA", "SHA256withECDSA",
ECNamedCurveTable.getParameterSpec("secp256k1"), ECNamedCurveTable.getParameterSpec("secp256k1"),
256, 256,
@ -123,7 +123,7 @@ object Crypto {
AlgorithmIdentifier(X9ObjectIdentifiers.ecdsa_with_SHA256, SECObjectIdentifiers.secp256r1), AlgorithmIdentifier(X9ObjectIdentifiers.ecdsa_with_SHA256, SECObjectIdentifiers.secp256r1),
listOf(AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, SECObjectIdentifiers.secp256r1)), listOf(AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, SECObjectIdentifiers.secp256r1)),
cordaBouncyCastleProvider.name, cordaBouncyCastleProvider.name,
"ECDSA", "EC",
"SHA256withECDSA", "SHA256withECDSA",
ECNamedCurveTable.getParameterSpec("secp256r1"), ECNamedCurveTable.getParameterSpec("secp256r1"),
256, 256,

View File

@ -2,7 +2,6 @@ package net.corda.core.crypto
import net.corda.core.KeepForDJVM import net.corda.core.KeepForDJVM
import net.corda.core.identity.AnonymousParty import net.corda.core.identity.AnonymousParty
import net.corda.core.serialization.CordaSerializable
import java.security.PublicKey import java.security.PublicKey
@KeepForDJVM @KeepForDJVM

View File

@ -1,6 +1,7 @@
package net.corda.core.flows package net.corda.core.flows
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable
import net.corda.core.CordaInternal
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.isFulfilledBy import net.corda.core.crypto.isFulfilledBy
import net.corda.core.identity.Party import net.corda.core.identity.Party
@ -45,6 +46,13 @@ class FinalityFlow private constructor(val transaction: SignedTransaction,
private val sessions: Collection<FlowSession>, private val sessions: Collection<FlowSession>,
private val newApi: Boolean, private val newApi: Boolean,
private val statesToRecord: StatesToRecord = ONLY_RELEVANT) : FlowLogic<SignedTransaction>() { private val statesToRecord: StatesToRecord = ONLY_RELEVANT) : FlowLogic<SignedTransaction>() {
@CordaInternal
data class ExtraConstructorArgs(val oldParticipants: Collection<Party>, val sessions: Collection<FlowSession>, val newApi: Boolean, val statesToRecord: StatesToRecord)
@CordaInternal
fun getExtraConstructorArgs() = ExtraConstructorArgs(oldParticipants, sessions, newApi, statesToRecord)
@Deprecated(DEPRECATION_MSG) @Deprecated(DEPRECATION_MSG)
constructor(transaction: SignedTransaction, extraRecipients: Set<Party>, progressTracker: ProgressTracker) : this( constructor(transaction: SignedTransaction, extraRecipients: Set<Party>, progressTracker: ProgressTracker) : this(
transaction, extraRecipients, progressTracker, emptyList(), false transaction, extraRecipients, progressTracker, emptyList(), false

View File

@ -1,7 +1,5 @@
package net.corda.core.flows package net.corda.core.flows
import net.corda.core.internal.ServiceHubCoreInternal
import net.corda.core.node.ServiceHub
import java.util.concurrent.CompletableFuture import java.util.concurrent.CompletableFuture
/** /**

View File

@ -24,9 +24,6 @@ import net.corda.core.messaging.DataFeed
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import net.corda.core.node.ServiceHub import net.corda.core.node.ServiceHub
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.SerializationDefaults
import net.corda.core.serialization.SerializedBytes
import net.corda.core.serialization.serialize
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.ProgressTracker
import net.corda.core.utilities.UntrustworthyData import net.corda.core.utilities.UntrustworthyData
@ -130,6 +127,32 @@ abstract class FlowLogic<out T> {
*/ */
val serviceHub: ServiceHub get() = stateMachine.serviceHub val serviceHub: ServiceHub get() = stateMachine.serviceHub
/**
* Returns `true` when the current [FlowLogic] has been killed (has received a command to halt its progress and terminate).
*
* Check this property in long-running computation loops to exit a flow that has been killed:
* ```
* while (!isKilled) {
* // do some computation
* }
* ```
*
* Ideal usage would include throwing a [KilledFlowException] which will lead to the termination of the flow:
* ```
* for (item in list) {
* if (isKilled) {
* throw KilledFlowException(runId)
* }
* // do some computation
* }
* ```
*
* Note, once the [isKilled] flag is set to `true` the flow may terminate once it reaches the next API function marked with the
* @[Suspendable] annotation. Therefore, it is possible to write a flow that does not interact with the [isKilled] flag while still
* terminating correctly.
*/
val isKilled: Boolean get() = stateMachine.isKilled
/** /**
* Creates a communication session with [destination]. Subsequently you may send/receive using this session object. How the messaging * Creates a communication session with [destination]. Subsequently you may send/receive using this session object. How the messaging
* is routed depends on the [Destination] type, including whether this call does any initial communication. * is routed depends on the [Destination] type, including whether this call does any initial communication.
@ -267,7 +290,7 @@ abstract class FlowLogic<out T> {
@Suspendable @Suspendable
internal fun <R : Any> FlowSession.sendAndReceiveWithRetry(receiveType: Class<R>, payload: Any): UntrustworthyData<R> { internal fun <R : Any> FlowSession.sendAndReceiveWithRetry(receiveType: Class<R>, payload: Any): UntrustworthyData<R> {
val request = FlowIORequest.SendAndReceive( val request = FlowIORequest.SendAndReceive(
sessionToMessage = mapOf(this to payload.serialize(context = SerializationDefaults.P2P_CONTEXT)), sessionToMessage = stateMachine.serialize(mapOf(this to payload)),
shouldRetrySend = true shouldRetrySend = true
) )
return stateMachine.suspend(request, maySkipCheckpoint = false)[this]!!.checkPayloadIs(receiveType) return stateMachine.suspend(request, maySkipCheckpoint = false)[this]!!.checkPayloadIs(receiveType)
@ -333,7 +356,7 @@ abstract class FlowLogic<out T> {
@JvmOverloads @JvmOverloads
fun sendAll(payload: Any, sessions: Set<FlowSession>, maySkipCheckpoint: Boolean = false) { fun sendAll(payload: Any, sessions: Set<FlowSession>, maySkipCheckpoint: Boolean = false) {
val sessionToPayload = sessions.map { it to payload }.toMap() val sessionToPayload = sessions.map { it to payload }.toMap()
return sendAll(sessionToPayload, maySkipCheckpoint) return sendAllMap(sessionToPayload, maySkipCheckpoint)
} }
/** /**
@ -348,23 +371,13 @@ abstract class FlowLogic<out T> {
*/ */
@Suspendable @Suspendable
@JvmOverloads @JvmOverloads
fun sendAll(payloadsPerSession: Map<FlowSession, Any>, maySkipCheckpoint: Boolean = false) { fun sendAllMap(payloadsPerSession: Map<FlowSession, Any>, maySkipCheckpoint: Boolean = false) {
val request = FlowIORequest.Send( val request = FlowIORequest.Send(
sessionToMessage = serializePayloads(payloadsPerSession) sessionToMessage = stateMachine.serialize(payloadsPerSession)
) )
stateMachine.suspend(request, maySkipCheckpoint) stateMachine.suspend(request, maySkipCheckpoint)
} }
@Suspendable
private fun serializePayloads(payloadsPerSession: Map<FlowSession, Any>): Map<FlowSession, SerializedBytes<Any>> {
val cachedSerializedPayloads = mutableMapOf<Any, SerializedBytes<Any>>()
return payloadsPerSession.mapValues { (_, payload) ->
cachedSerializedPayloads[payload] ?: payload.serialize(context = SerializationDefaults.P2P_CONTEXT).also { cachedSerializedPayloads[payload] = it }
}
}
/** /**
* Invokes the given subflow. This function returns once the subflow completes successfully with the result * Invokes the given subflow. This function returns once the subflow completes successfully with the result
* returned by that subflow's [call] method. If the subflow has a progress tracker, it is attached to the * returned by that subflow's [call] method. If the subflow has a progress tracker, it is attached to the
@ -380,10 +393,8 @@ abstract class FlowLogic<out T> {
@Suspendable @Suspendable
@Throws(FlowException::class) @Throws(FlowException::class)
open fun <R> subFlow(subLogic: FlowLogic<R>): R { open fun <R> subFlow(subLogic: FlowLogic<R>): R {
subLogic.stateMachine = stateMachine
maybeWireUpProgressTracking(subLogic)
logger.debug { "Calling subflow: $subLogic" } logger.debug { "Calling subflow: $subLogic" }
val result = stateMachine.subFlow(subLogic) val result = stateMachine.subFlow(this, subLogic)
logger.debug { "Subflow finished with result ${result.toString().abbreviate(300)}" } logger.debug { "Subflow finished with result ${result.toString().abbreviate(300)}" }
return result return result
} }
@ -540,18 +551,6 @@ abstract class FlowLogic<out T> {
_stateMachine = value _stateMachine = value
} }
private fun maybeWireUpProgressTracking(subLogic: FlowLogic<*>) {
val ours = progressTracker
val theirs = subLogic.progressTracker
if (ours != null && theirs != null && ours != theirs) {
if (ours.currentStep == ProgressTracker.UNSTARTED) {
logger.debug { "Initializing the progress tracker for flow: ${this::class.java.name}." }
ours.nextStep()
}
ours.setChildProgressTracker(ours.currentStep, theirs)
}
}
private fun enforceNoDuplicates(sessions: List<FlowSession>) { private fun enforceNoDuplicates(sessions: List<FlowSession>) {
require(sessions.size == sessions.toSet().size) { "A flow session can only appear once as argument." } require(sessions.size == sessions.toSet().size) { "A flow session can only appear once as argument." }
} }
@ -579,12 +578,7 @@ abstract class FlowLogic<out T> {
@Suspendable @Suspendable
fun <R : Any> await(operation: FlowExternalAsyncOperation<R>): R { fun <R : Any> await(operation: FlowExternalAsyncOperation<R>): R {
// Wraps the passed in [FlowExternalAsyncOperation] so its [CompletableFuture] can be converted into a [CordaFuture] // Wraps the passed in [FlowExternalAsyncOperation] so its [CompletableFuture] can be converted into a [CordaFuture]
val flowAsyncOperation = object : FlowAsyncOperation<R>, WrappedFlowExternalAsyncOperation<R> { val flowAsyncOperation = WrappedFlowExternalAsyncOperation(operation)
override val operation = operation
override fun execute(deduplicationId: String): CordaFuture<R> {
return this.operation.execute(deduplicationId).asCordaFuture()
}
}
val request = FlowIORequest.ExecuteAsyncOperation(flowAsyncOperation) val request = FlowIORequest.ExecuteAsyncOperation(flowAsyncOperation)
return stateMachine.suspend(request, false) return stateMachine.suspend(request, false)
} }
@ -598,9 +592,74 @@ abstract class FlowLogic<out T> {
*/ */
@Suspendable @Suspendable
fun <R : Any> await(operation: FlowExternalOperation<R>): R { fun <R : Any> await(operation: FlowExternalOperation<R>): R {
val flowAsyncOperation = object : FlowAsyncOperation<R>, WrappedFlowExternalOperation<R> { val flowAsyncOperation = WrappedFlowExternalOperation(serviceHub as ServiceHubCoreInternal, operation)
override val serviceHub = this@FlowLogic.serviceHub as ServiceHubCoreInternal val request = FlowIORequest.ExecuteAsyncOperation(flowAsyncOperation)
override val operation = operation return stateMachine.suspend(request, false)
}
/**
* Helper function that throws a [KilledFlowException] if the current [FlowLogic] has been killed.
*
* Call this function in long-running computation loops to exit a flow that has been killed:
* ```
* for (item in list) {
* checkFlowIsNotKilled()
* // do some computation
* }
* ```
*
* See the [isKilled] property for more information.
*/
fun checkFlowIsNotKilled() {
if (isKilled) {
throw KilledFlowException(runId)
}
}
/**
* Helper function that throws a [KilledFlowException] if the current [FlowLogic] has been killed. The provided message is added to the
* thrown [KilledFlowException].
*
* Call this function in long-running computation loops to exit a flow that has been killed:
* ```
* for (item in list) {
* checkFlowIsNotKilled { "The flow $runId was killed while iterating through the list of items" }
* // do some computation
* }
* ```
*
* See the [isKilled] property for more information.
*/
fun checkFlowIsNotKilled(lazyMessage: () -> Any) {
if (isKilled) {
val message = lazyMessage()
throw KilledFlowException(runId, message.toString())
}
}
}
/**
* [WrappedFlowExternalAsyncOperation] is added to allow jackson to properly reference the data stored within the wrapped
* [FlowExternalAsyncOperation].
*/
private class WrappedFlowExternalAsyncOperation<R : Any>(val operation: FlowExternalAsyncOperation<R>) : FlowAsyncOperation<R> {
override fun execute(deduplicationId: String): CordaFuture<R> {
return operation.execute(deduplicationId).asCordaFuture()
}
}
/**
* [WrappedFlowExternalOperation] is added to allow jackson to properly reference the data stored within the wrapped
* [FlowExternalOperation].
*
* The reference to [ServiceHub] is also needed by Kryo to properly keep a reference to [ServiceHub] so that
* [FlowExternalOperation] can be run from the [ServiceHubCoreInternal.externalOperationExecutor] without causing errors when retrying a
* flow. A [NullPointerException] is thrown if [FlowLogic.serviceHub] is accessed from [FlowLogic.await] when retrying a flow.
*/
private class WrappedFlowExternalOperation<R : Any>(
val serviceHub: ServiceHubCoreInternal,
val operation: FlowExternalOperation<R>
) : FlowAsyncOperation<R> {
override fun execute(deduplicationId: String): CordaFuture<R> { override fun execute(deduplicationId: String): CordaFuture<R> {
// Using a [CompletableFuture] allows unhandled exceptions to be thrown inside the background operation // Using a [CompletableFuture] allows unhandled exceptions to be thrown inside the background operation
// the exceptions will be set on the future by [CompletableFuture.AsyncSupply.run] // the exceptions will be set on the future by [CompletableFuture.AsyncSupply.run]
@ -609,31 +668,6 @@ abstract class FlowLogic<out T> {
serviceHub.externalOperationExecutor serviceHub.externalOperationExecutor
).asCordaFuture() ).asCordaFuture()
} }
}
val request = FlowIORequest.ExecuteAsyncOperation(flowAsyncOperation)
return stateMachine.suspend(request, false)
}
}
/**
* [WrappedFlowExternalAsyncOperation] is added to allow jackson to properly reference the data stored within the wrapped
* [FlowExternalAsyncOperation].
*/
private interface WrappedFlowExternalAsyncOperation<R : Any> {
val operation: FlowExternalAsyncOperation<R>
}
/**
* [WrappedFlowExternalOperation] is added to allow jackson to properly reference the data stored within the wrapped
* [FlowExternalOperation].
*
* The reference to [ServiceHub] is is also needed by Kryo to properly keep a reference to [ServiceHub] so that
* [FlowExternalOperation] can be run from the [ServiceHubCoreInternal.externalOperationExecutor] without causing errors when retrying a
* flow. A [NullPointerException] is thrown if [FlowLogic.serviceHub] is accessed from [FlowLogic.await] when retrying a flow.
*/
private interface WrappedFlowExternalOperation<R : Any> {
val serviceHub: ServiceHub
val operation: FlowExternalOperation<R>
} }
/** /**

View File

@ -0,0 +1,14 @@
package net.corda.core.flows
import net.corda.core.CordaRuntimeException
/**
* An exception that is thrown when a flow has been killed.
*
* This exception can be returned and thrown to RPC clients waiting for the result of a flow's future.
*
* It can also be used in conjunction with [FlowLogic.isKilled] to escape long-running computation loops when a flow has been killed.
*/
class KilledFlowException(val id: StateMachineRunId, message: String) : CordaRuntimeException(message) {
constructor(id: StateMachineRunId) : this(id, "The flow $id was killed")
}

View File

@ -28,7 +28,7 @@ import java.util.jar.JarInputStream
// *Internal* Corda-specific utilities. // *Internal* Corda-specific utilities.
const val PLATFORM_VERSION = 6 const val PLATFORM_VERSION = 7
fun ServicesForResolution.ensureMinimumPlatformVersion(requiredMinPlatformVersion: Int, feature: String) { fun ServicesForResolution.ensureMinimumPlatformVersion(requiredMinPlatformVersion: Int, feature: String) {
checkMinimumPlatformVersion(networkParameters.minimumPlatformVersion, requiredMinPlatformVersion, feature) checkMinimumPlatformVersion(networkParameters.minimumPlatformVersion, requiredMinPlatformVersion, feature)
@ -48,6 +48,20 @@ fun checkMinimumPlatformVersion(minimumPlatformVersion: Int, requiredMinPlatform
@Throws(NumberFormatException::class) @Throws(NumberFormatException::class)
fun getJavaUpdateVersion(javaVersion: String): Long = javaVersion.substringAfter("_").substringBefore("-").toLong() fun getJavaUpdateVersion(javaVersion: String): Long = javaVersion.substringAfter("_").substringBefore("-").toLong()
enum class JavaVersion(val versionString: String) {
Java_1_8("1.8"),
Java_11("11");
companion object {
fun isVersionAtLeast(version: JavaVersion): Boolean {
return currentVersion.toFloat() >= version.versionString.toFloat()
}
private val currentVersion: String = System.getProperty("java.specification.version") ?:
throw IllegalStateException("Unable to retrieve system property java.specification.version")
}
}
/** Provide access to internal method for AttachmentClassLoaderTests. */ /** Provide access to internal method for AttachmentClassLoaderTests. */
@DeleteForDJVM @DeleteForDJVM
fun TransactionBuilder.toWireTransaction(services: ServicesForResolution, serializationContext: SerializationContext): WireTransaction { fun TransactionBuilder.toWireTransaction(services: ServicesForResolution, serializationContext: SerializationContext): WireTransaction {

View File

@ -8,6 +8,7 @@ import net.corda.core.context.InvocationContext
import net.corda.core.flows.* import net.corda.core.flows.*
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.node.ServiceHub import net.corda.core.node.ServiceHub
import net.corda.core.serialization.SerializedBytes
import org.slf4j.Logger import org.slf4j.Logger
/** This is an internal interface that is implemented by code in the node module. You should look at [FlowLogic]. */ /** This is an internal interface that is implemented by code in the node module. You should look at [FlowLogic]. */
@ -17,6 +18,8 @@ interface FlowStateMachine<FLOWRETURN> {
@Suspendable @Suspendable
fun <SUSPENDRETURN : Any> suspend(ioRequest: FlowIORequest<SUSPENDRETURN>, maySkipCheckpoint: Boolean): SUSPENDRETURN fun <SUSPENDRETURN : Any> suspend(ioRequest: FlowIORequest<SUSPENDRETURN>, maySkipCheckpoint: Boolean): SUSPENDRETURN
fun serialize(payloads: Map<FlowSession, Any>): Map<FlowSession, SerializedBytes<Any>>
@Suspendable @Suspendable
fun initiateFlow(destination: Destination, wellKnownParty: Party): FlowSession fun initiateFlow(destination: Destination, wellKnownParty: Party): FlowSession
@ -25,7 +28,7 @@ interface FlowStateMachine<FLOWRETURN> {
fun recordAuditEvent(eventType: String, comment: String, extraAuditData: Map<String, String>) fun recordAuditEvent(eventType: String, comment: String, extraAuditData: Map<String, String>)
@Suspendable @Suspendable
fun <SUBFLOWRETURN> subFlow(subFlow: FlowLogic<SUBFLOWRETURN>): SUBFLOWRETURN fun <SUBFLOWRETURN> subFlow(currentFlow: FlowLogic<*>, subFlow: FlowLogic<SUBFLOWRETURN>): SUBFLOWRETURN
@Suspendable @Suspendable
fun flowStackSnapshot(flowClass: Class<out FlowLogic<*>>): FlowStackSnapshot? fun flowStackSnapshot(flowClass: Class<out FlowLogic<*>>): FlowStackSnapshot?
@ -44,4 +47,5 @@ interface FlowStateMachine<FLOWRETURN> {
val ourIdentity: Party val ourIdentity: Party
val ourSenderUUID: String? val ourSenderUUID: String?
val creationTime: Long val creationTime: Long
val isKilled: Boolean
} }

View File

@ -4,6 +4,7 @@ package net.corda.core.internal
import net.corda.core.DeleteForDJVM import net.corda.core.DeleteForDJVM
import net.corda.core.KeepForDJVM import net.corda.core.KeepForDJVM
import net.corda.core.StubOutForDJVM
import net.corda.core.crypto.Crypto import net.corda.core.crypto.Crypto
import net.corda.core.crypto.DigitalSignature import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
@ -417,6 +418,7 @@ fun <T, U : T> uncheckedCast(obj: T) = obj as U
fun <K, V> Iterable<Pair<K, V>>.toMultiMap(): Map<K, List<V>> = this.groupBy({ it.first }) { it.second } fun <K, V> Iterable<Pair<K, V>>.toMultiMap(): Map<K, List<V>> = this.groupBy({ it.first }) { it.second }
/** Returns the location of this class. */ /** Returns the location of this class. */
@get:StubOutForDJVM
val Class<*>.location: URL get() = protectionDomain.codeSource.location val Class<*>.location: URL get() = protectionDomain.codeSource.location
/** Convenience method to get the package name of a class literal. */ /** Convenience method to get the package name of a class literal. */

View File

@ -0,0 +1,10 @@
package net.corda.core.internal
/*
Constants for new features that can only be switched on at specific platform versions can be specified in this file.
The text constant describes the feature and the numeric specifies the platform version the feature is enabled at.
*/
object PlatformVersionSwitches {
const val REMOVE_NO_OVERLAP_RULE_FOR_REFERENCE_DATA_ATTACHMENTS = 7
const val ENABLE_P2P_COMPRESSION = 7
}

View File

@ -2,6 +2,7 @@ package net.corda.core.internal
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable
import net.corda.core.DeleteForDJVM import net.corda.core.DeleteForDJVM
import net.corda.core.internal.notary.NotaryService
import net.corda.core.node.ServiceHub import net.corda.core.node.ServiceHub
import net.corda.core.node.StatesToRecord import net.corda.core.node.StatesToRecord
import java.util.concurrent.ExecutorService import java.util.concurrent.ExecutorService
@ -14,6 +15,11 @@ interface ServiceHubCoreInternal : ServiceHub {
val attachmentTrustCalculator: AttachmentTrustCalculator val attachmentTrustCalculator: AttachmentTrustCalculator
/**
* Optional `NotaryService` which will be `null` for all non-Notary nodes.
*/
val notaryService: NotaryService?
fun createTransactionsResolver(flow: ResolveTransactionsFlow): TransactionsResolver fun createTransactionsResolver(flow: ResolveTransactionsFlow): TransactionsResolver
} }

View File

@ -77,7 +77,12 @@ fun <T : Any> deserialiseComponentGroup(componentGroups: List<ComponentGroup>,
try { try {
factory.deserialize(component, clazz.java, context) factory.deserialize(component, clazz.java, context)
} catch (e: MissingAttachmentsException) { } catch (e: MissingAttachmentsException) {
throw e /**
* [ServiceHub.signInitialTransaction] forgets to declare that
* it may throw any checked exceptions. Wrap this one inside
* an unchecked version to avoid breaking Java CorDapps.
*/
throw MissingAttachmentsRuntimeException(e.ids, e.message, e)
} catch (e: Exception) { } catch (e: Exception) {
throw TransactionDeserialisationException(groupEnum, internalIndex, e) throw TransactionDeserialisationException(groupEnum, internalIndex, e)
} }
@ -88,7 +93,7 @@ fun <T : Any> deserialiseComponentGroup(componentGroups: List<ComponentGroup>,
* Exception raised if an error was encountered while attempting to deserialise a component group in a transaction. * Exception raised if an error was encountered while attempting to deserialise a component group in a transaction.
*/ */
class TransactionDeserialisationException(groupEnum: ComponentGroupEnum, index: Int, cause: Exception): class TransactionDeserialisationException(groupEnum: ComponentGroupEnum, index: Int, cause: Exception):
Exception("Failed to deserialise group $groupEnum at index $index in transaction: ${cause.message}", cause) RuntimeException("Failed to deserialise group $groupEnum at index $index in transaction: ${cause.message}", cause)
/** /**
* Method to deserialise Commands from its two groups: * Method to deserialise Commands from its two groups:

View File

@ -4,7 +4,6 @@ package net.corda.core.internal
import net.corda.core.KeepForDJVM import net.corda.core.KeepForDJVM
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import org.bouncycastle.asn1.ASN1Encodable
import org.bouncycastle.asn1.ASN1ObjectIdentifier import org.bouncycastle.asn1.ASN1ObjectIdentifier
import org.bouncycastle.asn1.x500.AttributeTypeAndValue import org.bouncycastle.asn1.x500.AttributeTypeAndValue
import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x500.X500Name

View File

@ -14,6 +14,19 @@ abstract class NotaryService : SingletonSerializeAsToken() {
abstract val services: ServiceHub abstract val services: ServiceHub
abstract val notaryIdentityKey: PublicKey abstract val notaryIdentityKey: PublicKey
/**
* Interfaces for the request and result formats of queries supported by notary services. To
* implement a new query, you must:
*
* - Define data classes which implement the [Query.Request] and [Query.Result] interfaces
* - Add corresponding handling for the new classes within the notary service implementations
* that you want to support the query.
*/
interface Query {
interface Request
interface Result
}
abstract fun start() abstract fun start()
abstract fun stop() abstract fun stop()
@ -22,4 +35,18 @@ abstract class NotaryService : SingletonSerializeAsToken() {
* @param otherPartySession client [Party] making the request * @param otherPartySession client [Party] making the request
*/ */
abstract fun createServiceFlow(otherPartySession: FlowSession): FlowLogic<Void?> abstract fun createServiceFlow(otherPartySession: FlowSession): FlowLogic<Void?>
/**
* Processes a [Query.Request] and returns a [Query.Result].
*
* Note that this always throws an [UnsupportedOperationException] to handle notary
* implementations that do not support this functionality. This must be overridden by
* notary implementations wishing to support query functionality.
*
* Overrides of this function may themselves still throw an [UnsupportedOperationException],
* if they do not support specific query implementations
*/
open fun processQuery(query: Query.Request): Query.Result {
throw UnsupportedOperationException("Notary has not implemented query support")
}
} }

View File

@ -252,7 +252,7 @@ interface CordaRPCOps : RPCOps {
* Note: This operation may be restricted only to node administrators. * Note: This operation may be restricted only to node administrators.
* @param parametersHash hash of network parameters to accept * @param parametersHash hash of network parameters to accept
* @throws IllegalArgumentException if network map advertises update with different parameters hash then the one accepted by node's operator. * @throws IllegalArgumentException if network map advertises update with different parameters hash then the one accepted by node's operator.
* @throws IOException if failed to send the approval to network map * @throws [IOException] if failed to send the approval to network map
*/ */
// TODO This operation should be restricted to just node admins. // TODO This operation should be restricted to just node admins.
fun acceptNewNetworkParameters(parametersHash: SecureHash) fun acceptNewNetworkParameters(parametersHash: SecureHash)

View File

@ -380,6 +380,26 @@ interface ServiceHub : ServicesForResolution {
* When used within a flow, this session automatically forms part of the enclosing flow transaction boundary, * When used within a flow, this session automatically forms part of the enclosing flow transaction boundary,
* and thus queryable data will include everything committed as of the last checkpoint. * and thus queryable data will include everything committed as of the last checkpoint.
* *
* We want to make sure users have a restricted access to administrative functions, this function will return a [RestrictedConnection] instance.
* The following methods are blocked:
* - abort(executor: Executor?)
* - clearWarnings()
* - close()
* - commit()
* - setSavepoint()
* - setSavepoint(name : String?)
* - releaseSavepoint(savepoint: Savepoint?)
* - rollback()
* - rollback(savepoint: Savepoint?)
* - setCatalog(catalog : String?)
* - setTransactionIsolation(level: Int)
* - setTypeMap(map: MutableMap<String, Class<*>>?)
* - setHoldability(holdability: Int)
* - setSchema(schema: String?)
* - setNetworkTimeout(executor: Executor?, milliseconds: Int)
* - setAutoCommit(autoCommit: Boolean)
* - setReadOnly(readOnly: Boolean)
*
* @throws IllegalStateException if called outside of a transaction. * @throws IllegalStateException if called outside of a transaction.
* @return A [Connection] * @return A [Connection]
*/ */
@ -393,6 +413,24 @@ interface ServiceHub : ServicesForResolution {
* NOTE: Suspendable flow operations such as send, receive, subFlow and sleep, cannot be called within the lambda. * NOTE: Suspendable flow operations such as send, receive, subFlow and sleep, cannot be called within the lambda.
* *
* @param block a lambda function with access to an [EntityManager]. * @param block a lambda function with access to an [EntityManager].
*
* We want to make sure users have a restricted access to administrative functions.
* The following methods are blocked:
* - close()
* - unwrap(cls: Class<T>?)
* - getDelegate(): Any
* - getMetamodel()
* - joinTransaction()
* - lock(entity: Any?, lockMode: LockModeType?)
* - lock(entity: Any?, lockMode: LockModeType?, properties: MutableMap<String, Any>?)
* - setProperty(propertyName: String?, value: Any?)
*
* getTransaction returns a [RestrictedEntityTransaction] to prevent unsafe manipulation of a flow's underlying
* database transaction.
* The following methods are blocked:
* - begin()
* - commit()
* - rollback()
*/ */
fun <T : Any?> withEntityManager(block: EntityManager.() -> T): T fun <T : Any?> withEntityManager(block: EntityManager.() -> T): T
@ -404,6 +442,24 @@ interface ServiceHub : ServicesForResolution {
* NOTE: Suspendable flow operations such as send, receive, subFlow and sleep, cannot be called within the lambda. * NOTE: Suspendable flow operations such as send, receive, subFlow and sleep, cannot be called within the lambda.
* *
* @param block a lambda function with access to an [EntityManager]. * @param block a lambda function with access to an [EntityManager].
*
* We want to make sure users have a restricted access to administrative functions.
* The following methods are blocked:
* - close()
* - unwrap(cls: Class<T>?)
* - getDelegate(): Any
* - getMetamodel()
* - joinTransaction()
* - lock(entity: Any?, lockMode: LockModeType?)
* - lock(entity: Any?, lockMode: LockModeType?, properties: MutableMap<String, Any>?)
* - setProperty(propertyName: String?, value: Any?)
*
* getTransaction returns a [RestrictedEntityTransaction] to prevent unsafe manipulation of a flow's underlying
* database transaction.
* The following methods are blocked:
* - begin()
* - commit()
* - rollback()
*/ */
fun withEntityManager(block: Consumer<EntityManager>) fun withEntityManager(block: Consumer<EntityManager>)

View File

@ -8,7 +8,6 @@ import net.corda.core.contracts.ContractState
import net.corda.core.contracts.StateRef import net.corda.core.contracts.StateRef
import net.corda.core.contracts.UniqueIdentifier import net.corda.core.contracts.UniqueIdentifier
import net.corda.core.identity.AbstractParty import net.corda.core.identity.AbstractParty
import net.corda.core.identity.Party
import net.corda.core.node.services.Vault import net.corda.core.node.services.Vault
import net.corda.core.schemas.StatePersistable import net.corda.core.schemas.StatePersistable
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable

View File

@ -302,7 +302,7 @@ object Builder {
@JvmStatic @JvmStatic
@JvmOverloads @JvmOverloads
fun <R> FieldInfo.notEqual(value: R, exactMatch: Boolean = true) = predicate(Builder.equal(value, exactMatch)) fun <R> FieldInfo.notEqual(value: R, exactMatch: Boolean = true) = predicate(Builder.notEqual(value, exactMatch))
@JvmStatic @JvmStatic
@Deprecated("Does not support fields from a MappedSuperclass. Use equivalent on a FieldInfo.") @Deprecated("Does not support fields from a MappedSuperclass. Use equivalent on a FieldInfo.")

View File

@ -0,0 +1,17 @@
package net.corda.core.serialization
import net.corda.core.CordaRuntimeException
import net.corda.core.KeepForDJVM
import net.corda.core.node.services.AttachmentId
@KeepForDJVM
@CordaSerializable
class MissingAttachmentsRuntimeException(val ids: List<AttachmentId>, message: String?, cause: Throwable?)
: CordaRuntimeException(message, cause) {
@Suppress("unused")
constructor(ids: List<AttachmentId>, message: String?) : this(ids, message, null)
@Suppress("unused")
constructor(ids: List<AttachmentId>) : this(ids, null, null)
}

View File

@ -18,6 +18,7 @@ import java.io.ByteArrayOutputStream
import java.io.IOException import java.io.IOException
import java.io.InputStream import java.io.InputStream
import java.net.* import java.net.*
import java.security.Permission
import java.util.* import java.util.*
/** /**
@ -378,6 +379,15 @@ object AttachmentURLStreamHandlerFactory : URLStreamHandlerFactory {
private class AttachmentURLConnection(url: URL, private val attachment: Attachment) : URLConnection(url) { private class AttachmentURLConnection(url: URL, private val attachment: Attachment) : URLConnection(url) {
override fun getContentLengthLong(): Long = attachment.size.toLong() override fun getContentLengthLong(): Long = attachment.size.toLong()
override fun getInputStream(): InputStream = attachment.open() override fun getInputStream(): InputStream = attachment.open()
/**
* Define the permissions that [AttachmentsClassLoader] will need to
* use this [URL]. The attachment is stored in memory, and so we
* don't need any extra permissions here. But if we don't override
* [getPermission] then [AttachmentsClassLoader] will assign the
* default permission of ALL_PERMISSION to these classes'
* [java.security.ProtectionDomain]. This would be a security hole!
*/
override fun getPermission(): Permission? = null
override fun connect() { override fun connect() {
connected = true connected = true
} }

View File

@ -3,7 +3,6 @@ package net.corda.core.serialization.internal
import net.corda.core.DeleteForDJVM import net.corda.core.DeleteForDJVM
import net.corda.core.DoNotImplement import net.corda.core.DoNotImplement
import net.corda.core.KeepForDJVM import net.corda.core.KeepForDJVM
import net.corda.core.crypto.SecureHash
import net.corda.core.serialization.* import net.corda.core.serialization.*
import net.corda.core.utilities.ByteSequence import net.corda.core.utilities.ByteSequence
import net.corda.core.utilities.sequence import net.corda.core.utilities.sequence

View File

@ -134,7 +134,7 @@ open class TransactionBuilder(
* *
* @returns A new [WireTransaction] that will be unaffected by further changes to this [TransactionBuilder]. * @returns A new [WireTransaction] that will be unaffected by further changes to this [TransactionBuilder].
* *
* @throws ZoneVersionTooLowException if there are reference states and the zone minimum platform version is less than 4. * @throws [ZoneVersionTooLowException] if there are reference states and the zone minimum platform version is less than 4.
*/ */
@Throws(MissingContractAttachments::class) @Throws(MissingContractAttachments::class)
fun toWireTransaction(services: ServicesForResolution): WireTransaction = toWireTransactionWithContext(services, null) fun toWireTransaction(services: ServicesForResolution): WireTransaction = toWireTransactionWithContext(services, null)

View File

@ -317,7 +317,7 @@ class WireTransaction(componentGroups: List<ComponentGroup>, val privacySalt: Pr
/** /**
* Checks that the given signature matches one of the commands and that it is a correct signature over the tx. * Checks that the given signature matches one of the commands and that it is a correct signature over the tx.
* *
* @throws SignatureException if the signature didn't match the transaction contents. * @throws [SignatureException] if the signature didn't match the transaction contents.
* @throws IllegalArgumentException if the signature key doesn't appear in any command. * @throws IllegalArgumentException if the signature key doesn't appear in any command.
*/ */
fun checkSignature(sig: TransactionSignature) { fun checkSignature(sig: TransactionSignature) {

View File

@ -471,9 +471,9 @@ class CryptoUtilsTest {
val privKeyDecoded = Crypto.decodePrivateKey(privKey.encoded) val privKeyDecoded = Crypto.decodePrivateKey(privKey.encoded)
val pubKeyDecoded = Crypto.decodePublicKey(pubKey.encoded) val pubKeyDecoded = Crypto.decodePublicKey(pubKey.encoded)
assertEquals(privKeyDecoded.algorithm, "ECDSA") assertEquals(privKeyDecoded.algorithm, "EC")
assertEquals((privKeyDecoded as ECKey).parameters, ECNamedCurveTable.getParameterSpec("secp256k1")) assertEquals((privKeyDecoded as ECKey).parameters, ECNamedCurveTable.getParameterSpec("secp256k1"))
assertEquals(pubKeyDecoded.algorithm, "ECDSA") assertEquals(pubKeyDecoded.algorithm, "EC")
assertEquals((pubKeyDecoded as ECKey).parameters, ECNamedCurveTable.getParameterSpec("secp256k1")) assertEquals((pubKeyDecoded as ECKey).parameters, ECNamedCurveTable.getParameterSpec("secp256k1"))
} }
@ -481,9 +481,9 @@ class CryptoUtilsTest {
fun `ECDSA secp256r1 scheme finder by key type`() { fun `ECDSA secp256r1 scheme finder by key type`() {
val keyPairR1 = Crypto.generateKeyPair(ECDSA_SECP256R1_SHA256) val keyPairR1 = Crypto.generateKeyPair(ECDSA_SECP256R1_SHA256)
val (privR1, pubR1) = keyPairR1 val (privR1, pubR1) = keyPairR1
assertEquals(privR1.algorithm, "ECDSA") assertEquals(privR1.algorithm, "EC")
assertEquals((privR1 as ECKey).parameters, ECNamedCurveTable.getParameterSpec("secp256r1")) assertEquals((privR1 as ECKey).parameters, ECNamedCurveTable.getParameterSpec("secp256r1"))
assertEquals(pubR1.algorithm, "ECDSA") assertEquals(pubR1.algorithm, "EC")
assertEquals((pubR1 as ECKey).parameters, ECNamedCurveTable.getParameterSpec("secp256r1")) assertEquals((pubR1 as ECKey).parameters, ECNamedCurveTable.getParameterSpec("secp256r1"))
} }
@ -530,11 +530,11 @@ class CryptoUtilsTest {
val encodedPubK1 = pubK1.encoded val encodedPubK1 = pubK1.encoded
val decodedPrivK1 = Crypto.decodePrivateKey(encodedPrivK1) val decodedPrivK1 = Crypto.decodePrivateKey(encodedPrivK1)
assertEquals(decodedPrivK1.algorithm, "ECDSA") assertEquals(decodedPrivK1.algorithm, "EC")
assertEquals(decodedPrivK1, privK1) assertEquals(decodedPrivK1, privK1)
val decodedPubK1 = Crypto.decodePublicKey(encodedPubK1) val decodedPubK1 = Crypto.decodePublicKey(encodedPubK1)
assertEquals(decodedPubK1.algorithm, "ECDSA") assertEquals(decodedPubK1.algorithm, "EC")
assertEquals(decodedPubK1, pubK1) assertEquals(decodedPubK1, pubK1)
} }
@ -546,11 +546,11 @@ class CryptoUtilsTest {
val encodedPubR1 = pubR1.encoded val encodedPubR1 = pubR1.encoded
val decodedPrivR1 = Crypto.decodePrivateKey(encodedPrivR1) val decodedPrivR1 = Crypto.decodePrivateKey(encodedPrivR1)
assertEquals(decodedPrivR1.algorithm, "ECDSA") assertEquals(decodedPrivR1.algorithm, "EC")
assertEquals(decodedPrivR1, privR1) assertEquals(decodedPrivR1, privR1)
val decodedPubR1 = Crypto.decodePublicKey(encodedPubR1) val decodedPubR1 = Crypto.decodePublicKey(encodedPubR1)
assertEquals(decodedPubR1.algorithm, "ECDSA") assertEquals(decodedPubR1.algorithm, "EC")
assertEquals(decodedPubR1, pubR1) assertEquals(decodedPubR1, pubR1)
} }

View File

@ -0,0 +1,156 @@
package net.corda.core.node.services.vault
import net.corda.core.node.services.vault.Builder.`in`
import net.corda.core.node.services.vault.Builder.equal
import net.corda.core.node.services.vault.Builder.greaterThan
import net.corda.core.node.services.vault.Builder.greaterThanOrEqual
import net.corda.core.node.services.vault.Builder.isNull
import net.corda.core.node.services.vault.Builder.lessThan
import net.corda.core.node.services.vault.Builder.lessThanOrEqual
import net.corda.core.node.services.vault.Builder.like
import net.corda.core.node.services.vault.Builder.notEqual
import net.corda.core.node.services.vault.Builder.notIn
import net.corda.core.node.services.vault.Builder.notLike
import net.corda.core.node.services.vault.Builder.notNull
import net.corda.core.node.services.vault.ColumnPredicate.AggregateFunction
import net.corda.core.node.services.vault.ColumnPredicate.Between
import net.corda.core.node.services.vault.ColumnPredicate.BinaryComparison
import net.corda.core.node.services.vault.ColumnPredicate.CollectionExpression
import net.corda.core.node.services.vault.ColumnPredicate.EqualityComparison
import net.corda.core.node.services.vault.ColumnPredicate.Likeness
import net.corda.core.node.services.vault.ColumnPredicate.NullExpression
import net.corda.core.node.services.vault.CriteriaExpression.ColumnPredicateExpression
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.ObjectAssert
import org.junit.Test
import javax.persistence.Entity
class QueryCriteriaUtilsBuilderTest {
/** JPA Entity class needed by `getField` */
@Entity
private class TestEntity(val field: String)
/** Returns a `FieldInfo` object to work on */
private val fieldInfo: FieldInfo get() = getField("field", TestEntity::class.java)
/** Thrown for the `ColumnPredicate` types that have no `operator` field */
private class ColumnPredicateHasNoOperatorFieldException : Exception("This ColumnPredicate has no operator field")
/** Returns the `operator` for the given `ColumnPredicate` */
private fun ColumnPredicate<out Any?>.getOperator(): Operator = when (this) {
is AggregateFunction -> throw ColumnPredicateHasNoOperatorFieldException()
is Between -> throw ColumnPredicateHasNoOperatorFieldException()
is BinaryComparison<*> -> operator
is CollectionExpression -> operator
is EqualityComparison<*> -> operator
is Likeness -> operator
is NullExpression -> operator
}
/** Returns the `operator` for the given `ColumnPredicateExpression` */
private fun ColumnPredicateExpression<Any, *>.getOperator(): Operator = this.predicate.getOperator()
/** Assert that the `ColumnPredicateExpression` uses the given `Operator`. */
private fun <T : ColumnPredicateExpression<Any, C>, C> ObjectAssert<T>.usesOperator(operator: Operator) {
extracting {
assertThat(it.getOperator()).isEqualTo(operator)
}
}
/** Sample `String` value to pass to the predicate expression */
private val stringValue = ""
/** Sample `List` value to pass to the predicate expression */
private val listValue = emptyList<String>()
@Test(timeout = 500)
fun `equal predicate uses EQUAL operator`() {
assertThat(fieldInfo.equal(stringValue)).usesOperator(EqualityComparisonOperator.EQUAL)
}
@Test(timeout = 500)
fun `equal predicate (exactMatch=false) uses EQUAL_IGNORE_CASE operator`() {
assertThat(fieldInfo.equal(stringValue, exactMatch = false)).usesOperator(EqualityComparisonOperator.EQUAL_IGNORE_CASE)
}
@Test(timeout = 500)
fun `notEqual predicate uses NOT_EQUAL operator`() {
assertThat(fieldInfo.notEqual(stringValue)).usesOperator(EqualityComparisonOperator.NOT_EQUAL)
}
@Test(timeout = 500)
fun `notEqual predicate (exactMatch=false) uses NOT_EQUAL_IGNORE_CASE operator`() {
assertThat(fieldInfo.notEqual(stringValue, exactMatch = false)).usesOperator(EqualityComparisonOperator.NOT_EQUAL_IGNORE_CASE)
}
@Test(timeout = 500)
fun `lessThan predicate uses LESS_THAN operator`() {
assertThat(fieldInfo.lessThan(stringValue)).usesOperator(BinaryComparisonOperator.LESS_THAN)
}
@Test(timeout = 500)
fun `lessThanOrEqual predicate uses LESS_THAN_OR_EQUAL operator`() {
assertThat(fieldInfo.lessThanOrEqual(stringValue)).usesOperator(BinaryComparisonOperator.LESS_THAN_OR_EQUAL)
}
@Test(timeout = 500)
fun `greaterThan predicate uses GREATER_THAN operator`() {
assertThat(fieldInfo.greaterThan(stringValue)).usesOperator(BinaryComparisonOperator.GREATER_THAN)
}
@Test(timeout = 500)
fun `greaterThanOrEqual predicate uses GREATER_THAN_OR_EQUAL operator`() {
assertThat(fieldInfo.greaterThanOrEqual(stringValue)).usesOperator(BinaryComparisonOperator.GREATER_THAN_OR_EQUAL)
}
@Test(timeout = 500)
fun `in predicate uses IN operator`() {
assertThat(fieldInfo.`in`(listValue)).usesOperator(CollectionOperator.IN)
}
@Test(timeout = 500)
fun `in predicate (exactMatch=false) uses IN_IGNORE_CASE operator`() {
assertThat(fieldInfo.`in`(listValue, exactMatch = false)).usesOperator(CollectionOperator.IN_IGNORE_CASE)
}
@Test(timeout = 500)
fun `notIn predicate uses NOT_IN operator`() {
assertThat(fieldInfo.notIn(listValue)).usesOperator(CollectionOperator.NOT_IN)
}
@Test(timeout = 500)
fun `notIn predicate (exactMatch=false) uses NOT_IN_IGNORE_CASE operator`() {
assertThat(fieldInfo.notIn(listValue, exactMatch = false)).usesOperator(CollectionOperator.NOT_IN_IGNORE_CASE)
}
@Test(timeout = 500)
fun `like predicate uses LIKE operator`() {
assertThat(fieldInfo.like(stringValue)).usesOperator(LikenessOperator.LIKE)
}
@Test(timeout = 500)
fun `like predicate (exactMatch=false) uses LIKE_IGNORE_CASE operator`() {
assertThat(fieldInfo.like(stringValue, exactMatch = false)).usesOperator(LikenessOperator.LIKE_IGNORE_CASE)
}
@Test(timeout = 500)
fun `notLike predicate uses NOT_LIKE operator`() {
assertThat(fieldInfo.notLike(stringValue)).usesOperator(LikenessOperator.NOT_LIKE)
}
@Test(timeout = 500)
fun `notLike predicate (exactMatch=false) uses NOT_LIKE_IGNORE_CASE operator`() {
assertThat(fieldInfo.notLike(stringValue, exactMatch = false)).usesOperator(LikenessOperator.NOT_LIKE_IGNORE_CASE)
}
@Test(timeout = 500)
fun `isNull predicate uses IS_NULL operator`() {
assertThat(fieldInfo.isNull()).usesOperator(NullOperator.IS_NULL)
}
@Test(timeout = 500)
fun `notNull predicate uses NOT_NULL operator`() {
assertThat(fieldInfo.notNull()).usesOperator(NullOperator.NOT_NULL)
}
}

View File

@ -87,11 +87,9 @@
<ID>ComplexCondition:InternalUtils.kt$it.type == this &amp;&amp; it.isPublic &amp;&amp; it.isStatic &amp;&amp; it.isFinal</ID> <ID>ComplexCondition:InternalUtils.kt$it.type == this &amp;&amp; it.isPublic &amp;&amp; it.isStatic &amp;&amp; it.isFinal</ID>
<ID>ComplexCondition:Main.kt$Main$(hostname != null) &amp;&amp; (port != null) &amp;&amp; (username != null) &amp;&amp; (password != null)</ID> <ID>ComplexCondition:Main.kt$Main$(hostname != null) &amp;&amp; (port != null) &amp;&amp; (username != null) &amp;&amp; (password != null)</ID>
<ID>ComplexCondition:Schema.kt$obj == null || obj is DescribedType || obj is Binary || forGenericType(type).run { isPrimitive(this) || this == TopType }</ID> <ID>ComplexCondition:Schema.kt$obj == null || obj is DescribedType || obj is Binary || forGenericType(type).run { isPrimitive(this) || this == TopType }</ID>
<ID>ComplexCondition:TopLevelTransition.kt$TopLevelTransition$currentState.isTransactionTracked &amp;&amp; checkpoint.flowState is FlowState.Started &amp;&amp; checkpoint.flowState.flowIORequest is FlowIORequest.WaitForLedgerCommit &amp;&amp; checkpoint.flowState.flowIORequest.hash == event.transaction.id</ID>
<ID>ComplexCondition:WireTransaction.kt$WireTransaction$notary != null &amp;&amp; (inputs.isNotEmpty() || references.isNotEmpty() || timeWindow != null)</ID> <ID>ComplexCondition:WireTransaction.kt$WireTransaction$notary != null &amp;&amp; (inputs.isNotEmpty() || references.isNotEmpty() || timeWindow != null)</ID>
<ID>ComplexMethod:AMQPBridgeManager.kt$AMQPBridgeManager.AMQPBridge$private fun clientArtemisMessageHandler(artemisMessage: ClientMessage)</ID> <ID>ComplexMethod:AMQPBridgeManager.kt$AMQPBridgeManager.AMQPBridge$private fun clientArtemisMessageHandler(artemisMessage: ClientMessage)</ID>
<ID>ComplexMethod:AMQPBridgeTest.kt$AMQPBridgeTest$@Test(timeout=300_000) fun `test acked and nacked messages`()</ID> <ID>ComplexMethod:AMQPBridgeTest.kt$AMQPBridgeTest$@Test(timeout=300_000) fun `test acked and nacked messages`()</ID>
<ID>ComplexMethod:AMQPChannelHandler.kt$AMQPChannelHandler$override fun userEventTriggered(ctx: ChannelHandlerContext, evt: Any)</ID>
<ID>ComplexMethod:AMQPTypeIdentifierParser.kt$AMQPTypeIdentifierParser$// Make sure our inputs aren't designed to blow things up. private fun validate(typeString: String)</ID> <ID>ComplexMethod:AMQPTypeIdentifierParser.kt$AMQPTypeIdentifierParser$// Make sure our inputs aren't designed to blow things up. private fun validate(typeString: String)</ID>
<ID>ComplexMethod:ANSIProgressRenderer.kt$ANSIProgressRenderer$// Returns number of lines rendered. private fun renderLevel(ansi: Ansi, error: Boolean): Int</ID> <ID>ComplexMethod:ANSIProgressRenderer.kt$ANSIProgressRenderer$// Returns number of lines rendered. private fun renderLevel(ansi: Ansi, error: Boolean): Int</ID>
<ID>ComplexMethod:ANSIProgressRenderer.kt$ANSIProgressRenderer$@Synchronized protected fun draw(moveUp: Boolean, error: Throwable? = null)</ID> <ID>ComplexMethod:ANSIProgressRenderer.kt$ANSIProgressRenderer$@Synchronized protected fun draw(moveUp: Boolean, error: Throwable? = null)</ID>
@ -124,13 +122,10 @@
<ID>ComplexMethod:ConfigUtilities.kt$// For Iterables figure out the type parameter and apply the same logic as above on the individual elements. private fun Iterable&lt;*&gt;.toConfigIterable(field: Field): Iterable&lt;Any?&gt;</ID> <ID>ComplexMethod:ConfigUtilities.kt$// For Iterables figure out the type parameter and apply the same logic as above on the individual elements. private fun Iterable&lt;*&gt;.toConfigIterable(field: Field): Iterable&lt;Any?&gt;</ID>
<ID>ComplexMethod:ConfigUtilities.kt$// TODO Move this to KeyStoreConfigHelpers. fun MutualSslConfiguration.configureDevKeyAndTrustStores(myLegalName: CordaX500Name, signingCertificateStore: FileBasedCertificateStoreSupplier, certificatesDirectory: Path, cryptoService: CryptoService? = null)</ID> <ID>ComplexMethod:ConfigUtilities.kt$// TODO Move this to KeyStoreConfigHelpers. fun MutualSslConfiguration.configureDevKeyAndTrustStores(myLegalName: CordaX500Name, signingCertificateStore: FileBasedCertificateStoreSupplier, certificatesDirectory: Path, cryptoService: CryptoService? = null)</ID>
<ID>ComplexMethod:ConfigUtilities.kt$@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN") // Reflect over the fields of the receiver and generate a value Map that can use to create Config object. private fun Any.toConfigMap(): Map&lt;String, Any&gt;</ID> <ID>ComplexMethod:ConfigUtilities.kt$@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN") // Reflect over the fields of the receiver and generate a value Map that can use to create Config object. private fun Any.toConfigMap(): Map&lt;String, Any&gt;</ID>
<ID>ComplexMethod:ConfigUtilities.kt$private fun Config.getCollectionValue(path: String, type: KType, onUnknownKeys: (Set&lt;String&gt;, logger: Logger) -&gt; Unit, nestedPath: String?, baseDirectory: Path?): Collection&lt;Any&gt;</ID>
<ID>ComplexMethod:ConfigUtilities.kt$private fun Config.getSingleValue(path: String, type: KType, onUnknownKeys: (Set&lt;String&gt;, logger: Logger) -&gt; Unit, nestedPath: String?, baseDirectory: Path?): Any?</ID>
<ID>ComplexMethod:ConfigUtilities.kt$private fun convertValue(value: Any): Any</ID> <ID>ComplexMethod:ConfigUtilities.kt$private fun convertValue(value: Any): Any</ID>
<ID>ComplexMethod:ConnectionStateMachine.kt$ConnectionStateMachine$override fun onConnectionFinal(event: Event)</ID> <ID>ComplexMethod:ConnectionStateMachine.kt$ConnectionStateMachine$override fun onConnectionFinal(event: Event)</ID>
<ID>ComplexMethod:ConnectionStateMachine.kt$ConnectionStateMachine$override fun onDelivery(event: Event)</ID> <ID>ComplexMethod:ConnectionStateMachine.kt$ConnectionStateMachine$override fun onDelivery(event: Event)</ID>
<ID>ComplexMethod:ConstraintsUtils.kt$ fun AttachmentConstraint.canBeTransitionedFrom(input: AttachmentConstraint, attachment: ContractAttachment): Boolean</ID> <ID>ComplexMethod:ConstraintsUtils.kt$ fun AttachmentConstraint.canBeTransitionedFrom(input: AttachmentConstraint, attachment: ContractAttachment): Boolean</ID>
<ID>ComplexMethod:CordaCliWrapper.kt$fun CordaCliWrapper.start(args: Array&lt;String&gt;)</ID>
<ID>ComplexMethod:CordaPersistence.kt$CordaPersistence$private fun &lt;T&gt; inTopLevelTransaction(isolationLevel: TransactionIsolationLevel, recoverableFailureTolerance: Int, recoverAnyNestedSQLException: Boolean, statement: DatabaseTransaction.() -&gt; T): T</ID> <ID>ComplexMethod:CordaPersistence.kt$CordaPersistence$private fun &lt;T&gt; inTopLevelTransaction(isolationLevel: TransactionIsolationLevel, recoverableFailureTolerance: Int, recoverAnyNestedSQLException: Boolean, statement: DatabaseTransaction.() -&gt; T): T</ID>
<ID>ComplexMethod:CordaRPCClient.kt$CordaRPCClientConfiguration$override fun equals(other: Any?): Boolean</ID> <ID>ComplexMethod:CordaRPCClient.kt$CordaRPCClientConfiguration$override fun equals(other: Any?): Boolean</ID>
<ID>ComplexMethod:CordaRPCClientTest.kt$CordaRPCClientTest$@Test(timeout=300_000) fun `shutdown command stops the node`()</ID> <ID>ComplexMethod:CordaRPCClientTest.kt$CordaRPCClientTest$@Test(timeout=300_000) fun `shutdown command stops the node`()</ID>
@ -164,7 +159,6 @@
<ID>ComplexMethod:MerkleTransaction.kt$FilteredTransaction.Companion$ private fun filterWithFun(wtx: WireTransaction, filtering: Predicate&lt;Any&gt;): List&lt;FilteredComponentGroup&gt;</ID> <ID>ComplexMethod:MerkleTransaction.kt$FilteredTransaction.Companion$ private fun filterWithFun(wtx: WireTransaction, filtering: Predicate&lt;Any&gt;): List&lt;FilteredComponentGroup&gt;</ID>
<ID>ComplexMethod:NetworkBootstrapper.kt$NetworkBootstrapper$private fun bootstrap( directory: Path, cordappJars: List&lt;Path&gt;, copyCordapps: CopyCordapps, fromCordform: Boolean, networkParametersOverrides: NetworkParametersOverrides = NetworkParametersOverrides() )</ID> <ID>ComplexMethod:NetworkBootstrapper.kt$NetworkBootstrapper$private fun bootstrap( directory: Path, cordappJars: List&lt;Path&gt;, copyCordapps: CopyCordapps, fromCordform: Boolean, networkParametersOverrides: NetworkParametersOverrides = NetworkParametersOverrides() )</ID>
<ID>ComplexMethod:NetworkBootstrapper.kt$NetworkBootstrapper$private fun createNodeDirectoriesIfNeeded(directory: Path, fromCordform: Boolean): Boolean</ID> <ID>ComplexMethod:NetworkBootstrapper.kt$NetworkBootstrapper$private fun createNodeDirectoriesIfNeeded(directory: Path, fromCordform: Boolean): Boolean</ID>
<ID>ComplexMethod:NetworkMapUpdater.kt$NetworkMapUpdater$fun updateNetworkMapCache(): Duration</ID>
<ID>ComplexMethod:NetworkParametersReader.kt$NetworkParametersReader$fun read(): NetworkParametersAndSigned</ID> <ID>ComplexMethod:NetworkParametersReader.kt$NetworkParametersReader$fun read(): NetworkParametersAndSigned</ID>
<ID>ComplexMethod:NetworkRegistrationHelper.kt$NetworkRegistrationHelper$ private fun pollServerForCertificates(requestId: String): List&lt;X509Certificate&gt;</ID> <ID>ComplexMethod:NetworkRegistrationHelper.kt$NetworkRegistrationHelper$ private fun pollServerForCertificates(requestId: String): List&lt;X509Certificate&gt;</ID>
<ID>ComplexMethod:NewTransaction.kt$NewTransaction$fun show(window: Window)</ID> <ID>ComplexMethod:NewTransaction.kt$NewTransaction$fun show(window: Window)</ID>
@ -182,7 +176,6 @@
<ID>ComplexMethod:RPCClientProxyHandler.kt$RPCClientProxyHandler$// This is the general function that transforms a client side RPC to internal Artemis messages. override fun invoke(proxy: Any, method: Method, arguments: Array&lt;out Any?&gt;?): Any?</ID> <ID>ComplexMethod:RPCClientProxyHandler.kt$RPCClientProxyHandler$// This is the general function that transforms a client side RPC to internal Artemis messages. override fun invoke(proxy: Any, method: Method, arguments: Array&lt;out Any?&gt;?): Any?</ID>
<ID>ComplexMethod:RPCClientProxyHandler.kt$RPCClientProxyHandler$private fun attemptReconnect()</ID> <ID>ComplexMethod:RPCClientProxyHandler.kt$RPCClientProxyHandler$private fun attemptReconnect()</ID>
<ID>ComplexMethod:RPCServer.kt$RPCServer$private fun clientArtemisMessageHandler(artemisMessage: ClientMessage)</ID> <ID>ComplexMethod:RPCServer.kt$RPCServer$private fun clientArtemisMessageHandler(artemisMessage: ClientMessage)</ID>
<ID>ComplexMethod:ReconnectingCordaRPCOps.kt$ReconnectingCordaRPCOps.ErrorInterceptingHandler$ private fun doInvoke(method: Method, args: Array&lt;out Any&gt;?, maxNumberOfAttempts: Int): Any?</ID>
<ID>ComplexMethod:ReconnectingCordaRPCOps.kt$ReconnectingCordaRPCOps.ReconnectingRPCConnection$ private tailrec fun establishConnectionWithRetry( retryInterval: Duration, roundRobinIndex: Int = 0, retries: Int = -1 ): CordaRPCConnection?</ID> <ID>ComplexMethod:ReconnectingCordaRPCOps.kt$ReconnectingCordaRPCOps.ReconnectingRPCConnection$ private tailrec fun establishConnectionWithRetry( retryInterval: Duration, roundRobinIndex: Int = 0, retries: Int = -1 ): CordaRPCConnection?</ID>
<ID>ComplexMethod:RemoteTypeCarpenter.kt$SchemaBuildingRemoteTypeCarpenter$override fun carpent(typeInformation: RemoteTypeInformation): Type</ID> <ID>ComplexMethod:RemoteTypeCarpenter.kt$SchemaBuildingRemoteTypeCarpenter$override fun carpent(typeInformation: RemoteTypeInformation): Type</ID>
<ID>ComplexMethod:RpcReconnectTests.kt$RpcReconnectTests$ @Test(timeout=300_000) fun `test that the RPC client is able to reconnect and proceed after node failure, restart, or connection reset`()</ID> <ID>ComplexMethod:RpcReconnectTests.kt$RpcReconnectTests$ @Test(timeout=300_000) fun `test that the RPC client is able to reconnect and proceed after node failure, restart, or connection reset`()</ID>
@ -215,7 +208,6 @@
<ID>EmptyDefaultConstructor:FlowRetryTest.kt$AsyncRetryFlow$()</ID> <ID>EmptyDefaultConstructor:FlowRetryTest.kt$AsyncRetryFlow$()</ID>
<ID>EmptyDefaultConstructor:FlowRetryTest.kt$RetryFlow$()</ID> <ID>EmptyDefaultConstructor:FlowRetryTest.kt$RetryFlow$()</ID>
<ID>EmptyDefaultConstructor:FlowRetryTest.kt$ThrowingFlow$()</ID> <ID>EmptyDefaultConstructor:FlowRetryTest.kt$ThrowingFlow$()</ID>
<ID>EmptyElseBlock:CordaCliWrapper.kt${ }</ID>
<ID>EmptyIfBlock:ContentSignerBuilder.kt$ContentSignerBuilder.SignatureOutputStream$if (alreadySigned) throw IllegalStateException("Cannot write to already signed object")</ID> <ID>EmptyIfBlock:ContentSignerBuilder.kt$ContentSignerBuilder.SignatureOutputStream$if (alreadySigned) throw IllegalStateException("Cannot write to already signed object")</ID>
<ID>EmptyIfBlock:InMemoryIdentityService.kt$InMemoryIdentityService${ }</ID> <ID>EmptyIfBlock:InMemoryIdentityService.kt$InMemoryIdentityService${ }</ID>
<ID>EmptyKtFile:KryoHook.kt$.KryoHook.kt</ID> <ID>EmptyKtFile:KryoHook.kt$.KryoHook.kt</ID>
@ -327,11 +319,9 @@
<ID>ForbiddenComment:CustomSerializer.kt$CustomSerializer.SubClass$// TODO: should this be a custom serializer at all, or should it just be a plain AMQPSerializer?</ID> <ID>ForbiddenComment:CustomSerializer.kt$CustomSerializer.SubClass$// TODO: should this be a custom serializer at all, or should it just be a plain AMQPSerializer?</ID>
<ID>ForbiddenComment:CustomSerializer.kt$CustomSerializer.SubClass$// TODO: should this be empty or contain the schema of the super?</ID> <ID>ForbiddenComment:CustomSerializer.kt$CustomSerializer.SubClass$// TODO: should this be empty or contain the schema of the super?</ID>
<ID>ForbiddenComment:DbTransactionsResolver.kt$DbTransactionsResolver$// TODO: This approach has two problems. Analyze and resolve them:</ID> <ID>ForbiddenComment:DbTransactionsResolver.kt$DbTransactionsResolver$// TODO: This approach has two problems. Analyze and resolve them:</ID>
<ID>ForbiddenComment:DefaultKryoCustomizer.kt$DefaultKryoCustomizer$// TODO: re-organise registrations into logical groups before v1.0</ID>
<ID>ForbiddenComment:DigitalSignatureWithCert.kt$// TODO: Rename this to DigitalSignature.WithCert once we're happy for it to be public API. The methods will need documentation</ID> <ID>ForbiddenComment:DigitalSignatureWithCert.kt$// TODO: Rename this to DigitalSignature.WithCert once we're happy for it to be public API. The methods will need documentation</ID>
<ID>ForbiddenComment:DriverDSLImpl.kt$DriverDSLImpl$// TODO: Derive name from the full picked name, don't just wrap the common name</ID> <ID>ForbiddenComment:DriverDSLImpl.kt$DriverDSLImpl$// TODO: Derive name from the full picked name, don't just wrap the common name</ID>
<ID>ForbiddenComment:DriverDSLImpl.kt$DriverDSLImpl$//TODO: remove this once we can bundle quasar properly.</ID> <ID>ForbiddenComment:DriverDSLImpl.kt$DriverDSLImpl$//TODO: remove this once we can bundle quasar properly.</ID>
<ID>ForbiddenComment:DriverDSLImpl.kt$DriverDSLImpl.Companion$// TODO: There is pending work to fix this issue without custom blacklisting. See: https://r3-cev.atlassian.net/browse/CORDA-2164.</ID>
<ID>ForbiddenComment:DriverDSLImpl.kt$DriverDSLImpl.LocalNetworkMap$// TODO: this object will copy NodeInfo files from started nodes to other nodes additional-node-infos/</ID> <ID>ForbiddenComment:DriverDSLImpl.kt$DriverDSLImpl.LocalNetworkMap$// TODO: this object will copy NodeInfo files from started nodes to other nodes additional-node-infos/</ID>
<ID>ForbiddenComment:DummyFungibleContract.kt$DummyFungibleContract$// TODO: This doesn't work with the trader demo, so use the underlying key instead</ID> <ID>ForbiddenComment:DummyFungibleContract.kt$DummyFungibleContract$// TODO: This doesn't work with the trader demo, so use the underlying key instead</ID>
<ID>ForbiddenComment:E2ETestKeyManagementService.kt$E2ETestKeyManagementService$// TODO: A full KeyManagementService implementation needs to record activity to the Audit Service and to limit</ID> <ID>ForbiddenComment:E2ETestKeyManagementService.kt$E2ETestKeyManagementService$// TODO: A full KeyManagementService implementation needs to record activity to the Audit Service and to limit</ID>
@ -350,7 +340,6 @@
<ID>ForbiddenComment:FlowLogicRefFactoryImpl.kt$FlowLogicRefFactoryImpl$// TODO: This is used via RPC but it's probably better if we pass in argument names and values explicitly</ID> <ID>ForbiddenComment:FlowLogicRefFactoryImpl.kt$FlowLogicRefFactoryImpl$// TODO: This is used via RPC but it's probably better if we pass in argument names and values explicitly</ID>
<ID>ForbiddenComment:GenerateRpcSslCertsCli.kt$GenerateRpcSslCerts$// TODO: consider adding a password strength policy.</ID> <ID>ForbiddenComment:GenerateRpcSslCertsCli.kt$GenerateRpcSslCerts$// TODO: consider adding a password strength policy.</ID>
<ID>ForbiddenComment:GuiUtilities.kt$// TODO: This is a temporary fix for the UI to show the correct issuer identity, this will break when we start randomizing keys. More work is needed here when the identity work is done.</ID> <ID>ForbiddenComment:GuiUtilities.kt$// TODO: This is a temporary fix for the UI to show the correct issuer identity, this will break when we start randomizing keys. More work is needed here when the identity work is done.</ID>
<ID>ForbiddenComment:HibernateConfiguration.kt$HibernateConfiguration$// TODO: require mechanism to set schemaOptions (databaseSchema, tablePrefix) which are not global to session</ID>
<ID>ForbiddenComment:IRS.kt$FloatingRatePaymentEvent$// TODO: Should an uncalculated amount return a zero ? null ? etc.</ID> <ID>ForbiddenComment:IRS.kt$FloatingRatePaymentEvent$// TODO: Should an uncalculated amount return a zero ? null ? etc.</ID>
<ID>ForbiddenComment:IRS.kt$InterestRateSwap$// TODO: Confirm: would someone really enter a swap with a negative fixed rate?</ID> <ID>ForbiddenComment:IRS.kt$InterestRateSwap$// TODO: Confirm: would someone really enter a swap with a negative fixed rate?</ID>
<ID>ForbiddenComment:IRS.kt$InterestRateSwap$// TODO: further tests</ID> <ID>ForbiddenComment:IRS.kt$InterestRateSwap$// TODO: further tests</ID>
@ -388,6 +377,7 @@
<ID>ForbiddenComment:LegalNameValidator.kt$LegalNameValidator.Rule.Companion$// TODO: Implement confusable character detection if we add more scripts.</ID> <ID>ForbiddenComment:LegalNameValidator.kt$LegalNameValidator.Rule.Companion$// TODO: Implement confusable character detection if we add more scripts.</ID>
<ID>ForbiddenComment:LocalTypeInformationBuilder.kt$// TODO: Revisit this when Kotlin issue is fixed.</ID> <ID>ForbiddenComment:LocalTypeInformationBuilder.kt$// TODO: Revisit this when Kotlin issue is fixed.</ID>
<ID>ForbiddenComment:LoggingBuyerFlow.kt$LoggingBuyerFlow$// TODO: This is potentially very expensive, and requires transaction details we may no longer have once</ID> <ID>ForbiddenComment:LoggingBuyerFlow.kt$LoggingBuyerFlow$// TODO: This is potentially very expensive, and requires transaction details we may no longer have once</ID>
<ID>ForbiddenComment:LoopbackBridgeManager.kt$LoopbackBridgeManager.LoopbackBridge$// TODO: refactor MDC support, duplicated in AMQPBridgeManager.</ID>
<ID>ForbiddenComment:MockServices.kt$MockServices.Companion$// TODO: Can we use an X509 principal generator here?</ID> <ID>ForbiddenComment:MockServices.kt$MockServices.Companion$// TODO: Can we use an X509 principal generator here?</ID>
<ID>ForbiddenComment:NetParams.kt$NetParamsSigner$// TODO: not supported</ID> <ID>ForbiddenComment:NetParams.kt$NetParamsSigner$// TODO: not supported</ID>
<ID>ForbiddenComment:NetworkBootstrapper.kt$NetworkBootstrapper$// TODO: pass a commandline parameter to the bootstrapper instead. Better yet, a notary config map</ID> <ID>ForbiddenComment:NetworkBootstrapper.kt$NetworkBootstrapper$// TODO: pass a commandline parameter to the bootstrapper instead. Better yet, a notary config map</ID>
@ -630,8 +620,6 @@
<ID>FunctionNaming:VersionExtractorTest.kt$VersionExtractorTest$@Test(timeout=300_000) fun version_header_extraction_no_metadata()</ID> <ID>FunctionNaming:VersionExtractorTest.kt$VersionExtractorTest$@Test(timeout=300_000) fun version_header_extraction_no_metadata()</ID>
<ID>FunctionNaming:VersionExtractorTest.kt$VersionExtractorTest$@Test(timeout=300_000) fun version_header_extraction_no_value()</ID> <ID>FunctionNaming:VersionExtractorTest.kt$VersionExtractorTest$@Test(timeout=300_000) fun version_header_extraction_no_value()</ID>
<ID>FunctionNaming:VersionExtractorTest.kt$VersionExtractorTest$@Test(timeout=300_000) fun version_header_extraction_present()</ID> <ID>FunctionNaming:VersionExtractorTest.kt$VersionExtractorTest$@Test(timeout=300_000) fun version_header_extraction_present()</ID>
<ID>FunctionNaming:VersionedParsingExampleTest.kt$VersionedParsingExampleTest$@Test(timeout=300_000) fun correct_parsing_function_is_used_for_present_version()</ID>
<ID>FunctionNaming:VersionedParsingExampleTest.kt$VersionedParsingExampleTest$@Test(timeout=300_000) fun default_value_is_used_for_absent_version()</ID>
<ID>LargeClass:AbstractNode.kt$AbstractNode&lt;S&gt; : SingletonSerializeAsToken</ID> <ID>LargeClass:AbstractNode.kt$AbstractNode&lt;S&gt; : SingletonSerializeAsToken</ID>
<ID>LargeClass:SingleThreadedStateMachineManager.kt$SingleThreadedStateMachineManager : StateMachineManagerStateMachineManagerInternal</ID> <ID>LargeClass:SingleThreadedStateMachineManager.kt$SingleThreadedStateMachineManager : StateMachineManagerStateMachineManagerInternal</ID>
<ID>LongMethod:FlowCookbook.kt$InitiatorFlow$@Suppress("RemoveExplicitTypeArguments") @Suspendable override fun call()</ID> <ID>LongMethod:FlowCookbook.kt$InitiatorFlow$@Suppress("RemoveExplicitTypeArguments") @Suspendable override fun call()</ID>
@ -643,8 +631,8 @@
<ID>LongParameterList:AbstractNode.kt$(databaseConfig: DatabaseConfig, wellKnownPartyFromX500Name: (CordaX500Name) -&gt; Party?, wellKnownPartyFromAnonymous: (AbstractParty) -&gt; Party?, schemaService: SchemaService, hikariProperties: Properties, cacheFactory: NamedCacheFactory, customClassLoader: ClassLoader?)</ID> <ID>LongParameterList:AbstractNode.kt$(databaseConfig: DatabaseConfig, wellKnownPartyFromX500Name: (CordaX500Name) -&gt; Party?, wellKnownPartyFromAnonymous: (AbstractParty) -&gt; Party?, schemaService: SchemaService, hikariProperties: Properties, cacheFactory: NamedCacheFactory, customClassLoader: ClassLoader?)</ID>
<ID>LongParameterList:AbstractNode.kt$(hikariProperties: Properties, databaseConfig: DatabaseConfig, schemas: Set&lt;MappedSchema&gt;, metricRegistry: MetricRegistry? = null, cordappLoader: CordappLoader? = null, currentDir: Path? = null, ourName: CordaX500Name)</ID> <ID>LongParameterList:AbstractNode.kt$(hikariProperties: Properties, databaseConfig: DatabaseConfig, schemas: Set&lt;MappedSchema&gt;, metricRegistry: MetricRegistry? = null, cordappLoader: CordappLoader? = null, currentDir: Path? = null, ourName: CordaX500Name)</ID>
<ID>LongParameterList:ArtemisMessagingServer.kt$ArtemisMessagingServer$(name: String, send: Boolean = false, consume: Boolean = false, createDurableQueue: Boolean = false, deleteDurableQueue: Boolean = false, createNonDurableQueue: Boolean = false, deleteNonDurableQueue: Boolean = false, manage: Boolean = false, browse: Boolean = false)</ID> <ID>LongParameterList:ArtemisMessagingServer.kt$ArtemisMessagingServer$(name: String, send: Boolean = false, consume: Boolean = false, createDurableQueue: Boolean = false, deleteDurableQueue: Boolean = false, createNonDurableQueue: Boolean = false, deleteNonDurableQueue: Boolean = false, manage: Boolean = false, browse: Boolean = false)</ID>
<ID>LongParameterList:ArtemisRpcBroker.kt$ArtemisRpcBroker.Companion$(configuration: MutualSslConfiguration, address: NetworkHostAndPort, adminAddress: NetworkHostAndPort, securityManager: RPCSecurityManager, maxMessageSize: Int, jmxEnabled: Boolean, baseDirectory: Path, shouldStartLocalShell: Boolean)</ID> <ID>LongParameterList:ArtemisRpcBroker.kt$ArtemisRpcBroker.Companion$(configuration: MutualSslConfiguration, address: NetworkHostAndPort, adminAddress: NetworkHostAndPort, securityManager: RPCSecurityManager, maxMessageSize: Int, journalBufferTimeout: Int?, jmxEnabled: Boolean, baseDirectory: Path, shouldStartLocalShell: Boolean)</ID>
<ID>LongParameterList:ArtemisRpcBroker.kt$ArtemisRpcBroker.Companion$(configuration: MutualSslConfiguration, address: NetworkHostAndPort, adminAddress: NetworkHostAndPort, sslOptions: BrokerRpcSslOptions, securityManager: RPCSecurityManager, maxMessageSize: Int, jmxEnabled: Boolean, baseDirectory: Path, shouldStartLocalShell: Boolean)</ID> <ID>LongParameterList:ArtemisRpcBroker.kt$ArtemisRpcBroker.Companion$(configuration: MutualSslConfiguration, address: NetworkHostAndPort, adminAddress: NetworkHostAndPort, sslOptions: BrokerRpcSslOptions, securityManager: RPCSecurityManager, maxMessageSize: Int, journalBufferTimeout: Int?, jmxEnabled: Boolean, baseDirectory: Path, shouldStartLocalShell: Boolean)</ID>
<ID>LongParameterList:ArtemisRpcTests.kt$ArtemisRpcTests$(nodeSSlconfig: MutualSslConfiguration, brokerSslOptions: BrokerRpcSslOptions?, useSslForBroker: Boolean, clientSslOptions: ClientRpcSslOptions?, address: NetworkHostAndPort = ports.nextHostAndPort(), adminAddress: NetworkHostAndPort = ports.nextHostAndPort(), baseDirectory: Path = tempFolder.root.toPath() )</ID> <ID>LongParameterList:ArtemisRpcTests.kt$ArtemisRpcTests$(nodeSSlconfig: MutualSslConfiguration, brokerSslOptions: BrokerRpcSslOptions?, useSslForBroker: Boolean, clientSslOptions: ClientRpcSslOptions?, address: NetworkHostAndPort = ports.nextHostAndPort(), adminAddress: NetworkHostAndPort = ports.nextHostAndPort(), baseDirectory: Path = tempFolder.root.toPath() )</ID>
<ID>LongParameterList:AttachmentsClassLoader.kt$AttachmentsClassLoaderBuilder$(attachments: List&lt;Attachment&gt;, params: NetworkParameters, txId: SecureHash, isAttachmentTrusted: (Attachment) -&gt; Boolean, parent: ClassLoader = ClassLoader.getSystemClassLoader(), block: (ClassLoader) -&gt; T)</ID> <ID>LongParameterList:AttachmentsClassLoader.kt$AttachmentsClassLoaderBuilder$(attachments: List&lt;Attachment&gt;, params: NetworkParameters, txId: SecureHash, isAttachmentTrusted: (Attachment) -&gt; Boolean, parent: ClassLoader = ClassLoader.getSystemClassLoader(), block: (ClassLoader) -&gt; T)</ID>
<ID>LongParameterList:BFTSmart.kt$BFTSmart.Replica$( states: List&lt;StateRef&gt;, txId: SecureHash, callerName: CordaX500Name, requestSignature: NotarisationRequestSignature, timeWindow: TimeWindow?, references: List&lt;StateRef&gt; = emptyList() )</ID> <ID>LongParameterList:BFTSmart.kt$BFTSmart.Replica$( states: List&lt;StateRef&gt;, txId: SecureHash, callerName: CordaX500Name, requestSignature: NotarisationRequestSignature, timeWindow: TimeWindow?, references: List&lt;StateRef&gt; = emptyList() )</ID>
@ -655,7 +643,6 @@
<ID>LongParameterList:CertificateRevocationListNodeTests.kt$CertificateRevocationListNodeTests$(port: Int, name: CordaX500Name = ALICE_NAME, crlCheckSoftFail: Boolean, nodeCrlDistPoint: String = "http://${server.hostAndPort}/crl/node.crl", tlsCrlDistPoint: String? = "http://${server.hostAndPort}/crl/empty.crl", maxMessageSize: Int = MAX_MESSAGE_SIZE)</ID> <ID>LongParameterList:CertificateRevocationListNodeTests.kt$CertificateRevocationListNodeTests$(port: Int, name: CordaX500Name = ALICE_NAME, crlCheckSoftFail: Boolean, nodeCrlDistPoint: String = "http://${server.hostAndPort}/crl/node.crl", tlsCrlDistPoint: String? = "http://${server.hostAndPort}/crl/empty.crl", maxMessageSize: Int = MAX_MESSAGE_SIZE)</ID>
<ID>LongParameterList:CertificateRevocationListNodeTests.kt$CertificateRevocationListNodeTests.Companion$(clrServer: CrlServer, signatureAlgorithm: String, caCertificate: X509Certificate, caPrivateKey: PrivateKey, endpoint: String, indirect: Boolean, vararg serialNumbers: BigInteger)</ID> <ID>LongParameterList:CertificateRevocationListNodeTests.kt$CertificateRevocationListNodeTests.Companion$(clrServer: CrlServer, signatureAlgorithm: String, caCertificate: X509Certificate, caPrivateKey: PrivateKey, endpoint: String, indirect: Boolean, vararg serialNumbers: BigInteger)</ID>
<ID>LongParameterList:CertificateStoreStubs.kt$CertificateStoreStubs.P2P.Companion$(baseDirectory: Path, certificatesDirectoryName: String = DEFAULT_CERTIFICATES_DIRECTORY_NAME, keyStoreFileName: String = KeyStore.DEFAULT_STORE_FILE_NAME, keyStorePassword: String = KeyStore.DEFAULT_STORE_PASSWORD, keyPassword: String = keyStorePassword, trustStoreFileName: String = TrustStore.DEFAULT_STORE_FILE_NAME, trustStorePassword: String = TrustStore.DEFAULT_STORE_PASSWORD)</ID> <ID>LongParameterList:CertificateStoreStubs.kt$CertificateStoreStubs.P2P.Companion$(baseDirectory: Path, certificatesDirectoryName: String = DEFAULT_CERTIFICATES_DIRECTORY_NAME, keyStoreFileName: String = KeyStore.DEFAULT_STORE_FILE_NAME, keyStorePassword: String = KeyStore.DEFAULT_STORE_PASSWORD, keyPassword: String = keyStorePassword, trustStoreFileName: String = TrustStore.DEFAULT_STORE_FILE_NAME, trustStorePassword: String = TrustStore.DEFAULT_STORE_PASSWORD)</ID>
<ID>LongParameterList:CertificateStoreStubs.kt$CertificateStoreStubs.P2P.Companion$(certificatesDirectory: Path, keyStoreFileName: String = KeyStore.DEFAULT_STORE_FILE_NAME, keyStorePassword: String = KeyStore.DEFAULT_STORE_PASSWORD, keyPassword: String = keyStorePassword, trustStoreFileName: String = TrustStore.DEFAULT_STORE_FILE_NAME, trustStorePassword: String = TrustStore.DEFAULT_STORE_PASSWORD, trustStoreKeyPassword: String = TrustStore.DEFAULT_KEY_PASSWORD, @Suppress("UNUSED_PARAMETER") useOpenSsl: Boolean = false)</ID>
<ID>LongParameterList:ContractAttachment.kt$ContractAttachment.Companion$(attachment: Attachment, contract: ContractClassName, additionalContracts: Set&lt;ContractClassName&gt; = emptySet(), uploader: String? = null, signerKeys: List&lt;PublicKey&gt; = emptyList(), version: Int = DEFAULT_CORDAPP_VERSION)</ID> <ID>LongParameterList:ContractAttachment.kt$ContractAttachment.Companion$(attachment: Attachment, contract: ContractClassName, additionalContracts: Set&lt;ContractClassName&gt; = emptySet(), uploader: String? = null, signerKeys: List&lt;PublicKey&gt; = emptyList(), version: Int = DEFAULT_CORDAPP_VERSION)</ID>
<ID>LongParameterList:ContractFunctions.kt$(expiry: String, notional: BigDecimal, strike: BigDecimal, foreignCurrency: Currency, domesticCurrency: Currency, partyA: Party, partyB: Party)</ID> <ID>LongParameterList:ContractFunctions.kt$(expiry: String, notional: BigDecimal, strike: BigDecimal, foreignCurrency: Currency, domesticCurrency: Currency, partyA: Party, partyB: Party)</ID>
<ID>LongParameterList:ContractFunctions.kt$(expiry: String, notional: Long, strike: Double, foreignCurrency: Currency, domesticCurrency: Currency, partyA: Party, partyB: Party)</ID> <ID>LongParameterList:ContractFunctions.kt$(expiry: String, notional: Long, strike: Double, foreignCurrency: Currency, domesticCurrency: Currency, partyA: Party, partyB: Party)</ID>
@ -925,7 +912,6 @@
<ID>MagicNumber:IrsDemoWebApplication.kt$IrsDemoWebApplication$1000</ID> <ID>MagicNumber:IrsDemoWebApplication.kt$IrsDemoWebApplication$1000</ID>
<ID>MagicNumber:JarScanningCordappLoader.kt$CordappLoaderTemplate$36</ID> <ID>MagicNumber:JarScanningCordappLoader.kt$CordappLoaderTemplate$36</ID>
<ID>MagicNumber:JarScanningCordappLoader.kt$CordappLoaderTemplate$64</ID> <ID>MagicNumber:JarScanningCordappLoader.kt$CordappLoaderTemplate$64</ID>
<ID>MagicNumber:JarScanningCordappLoader.kt$JarScanningCordappLoader$1000</ID>
<ID>MagicNumber:JarSignatureCollector.kt$JarSignatureCollector$1024</ID> <ID>MagicNumber:JarSignatureCollector.kt$JarSignatureCollector$1024</ID>
<ID>MagicNumber:JarSignatureTestUtils.kt$JarSignatureTestUtils$14</ID> <ID>MagicNumber:JarSignatureTestUtils.kt$JarSignatureTestUtils$14</ID>
<ID>MagicNumber:KMSUtils.kt$3650</ID> <ID>MagicNumber:KMSUtils.kt$3650</ID>
@ -1207,7 +1193,6 @@
<ID>MatchingDeclarationName:ConfigException.kt$net.corda.core.cordapp.ConfigException.kt</ID> <ID>MatchingDeclarationName:ConfigException.kt$net.corda.core.cordapp.ConfigException.kt</ID>
<ID>MatchingDeclarationName:ConfigUtilities.kt$net.corda.node.services.config.ConfigUtilities.kt</ID> <ID>MatchingDeclarationName:ConfigUtilities.kt$net.corda.node.services.config.ConfigUtilities.kt</ID>
<ID>MatchingDeclarationName:ContractsDSL.kt$net.corda.core.contracts.ContractsDSL.kt</ID> <ID>MatchingDeclarationName:ContractsDSL.kt$net.corda.core.contracts.ContractsDSL.kt</ID>
<ID>MatchingDeclarationName:CordaUtils.kt$net.corda.core.internal.CordaUtils.kt</ID>
<ID>MatchingDeclarationName:CurrencyParameterSensitivitySerialiser.kt$net.corda.vega.plugin.customserializers.CurrencyParameterSensitivitySerialiser.kt</ID> <ID>MatchingDeclarationName:CurrencyParameterSensitivitySerialiser.kt$net.corda.vega.plugin.customserializers.CurrencyParameterSensitivitySerialiser.kt</ID>
<ID>MatchingDeclarationName:FinanceWorkflowsUtils.kt$net.corda.finance.workflows.utils.FinanceWorkflowsUtils.kt</ID> <ID>MatchingDeclarationName:FinanceWorkflowsUtils.kt$net.corda.finance.workflows.utils.FinanceWorkflowsUtils.kt</ID>
<ID>MatchingDeclarationName:FlowStackSnapshot.kt$net.corda.testing.internal.FlowStackSnapshot.kt</ID> <ID>MatchingDeclarationName:FlowStackSnapshot.kt$net.corda.testing.internal.FlowStackSnapshot.kt</ID>
@ -1229,8 +1214,6 @@
<ID>MatchingDeclarationName:Query.kt$net.corda.webserver.api.Query.kt</ID> <ID>MatchingDeclarationName:Query.kt$net.corda.webserver.api.Query.kt</ID>
<ID>MatchingDeclarationName:ReceiveAllFlowTests.kt$net.corda.coretests.flows.ReceiveAllFlowTests.kt</ID> <ID>MatchingDeclarationName:ReceiveAllFlowTests.kt$net.corda.coretests.flows.ReceiveAllFlowTests.kt</ID>
<ID>MatchingDeclarationName:ReferenceInputStateTests.kt$net.corda.coretests.transactions.ReferenceInputStateTests.kt</ID> <ID>MatchingDeclarationName:ReferenceInputStateTests.kt$net.corda.coretests.transactions.ReferenceInputStateTests.kt</ID>
<ID>MatchingDeclarationName:RigorousMock.kt$net.corda.testing.internal.RigorousMock.kt</ID>
<ID>MatchingDeclarationName:RpcServerCordaFutureSerialiser.kt$net.corda.node.serialization.amqp.RpcServerCordaFutureSerialiser.kt</ID>
<ID>MatchingDeclarationName:SSLHelper.kt$net.corda.nodeapi.internal.protonwrapper.netty.SSLHelper.kt</ID> <ID>MatchingDeclarationName:SSLHelper.kt$net.corda.nodeapi.internal.protonwrapper.netty.SSLHelper.kt</ID>
<ID>MatchingDeclarationName:SampleData.kt$net.corda.deterministic.verifier.SampleData.kt</ID> <ID>MatchingDeclarationName:SampleData.kt$net.corda.deterministic.verifier.SampleData.kt</ID>
<ID>MatchingDeclarationName:SerializationHelper.kt$net.corda.networkbuilder.serialization.SerializationHelper.kt</ID> <ID>MatchingDeclarationName:SerializationHelper.kt$net.corda.networkbuilder.serialization.SerializationHelper.kt</ID>
@ -1285,7 +1268,6 @@
<ID>NestedBlockDepth:RPCClientProxyHandler.kt$RPCClientProxyHandler$// The handler for Artemis messages. private fun artemisMessageHandler(message: ClientMessage)</ID> <ID>NestedBlockDepth:RPCClientProxyHandler.kt$RPCClientProxyHandler$// The handler for Artemis messages. private fun artemisMessageHandler(message: ClientMessage)</ID>
<ID>NestedBlockDepth:ShutdownManager.kt$ShutdownManager$fun shutdown()</ID> <ID>NestedBlockDepth:ShutdownManager.kt$ShutdownManager$fun shutdown()</ID>
<ID>NestedBlockDepth:SpringDriver.kt$SpringBootDriverDSL$private fun queryWebserver(handle: NodeHandle, process: Process, checkUrl: String): WebserverHandle</ID> <ID>NestedBlockDepth:SpringDriver.kt$SpringBootDriverDSL$private fun queryWebserver(handle: NodeHandle, process: Process, checkUrl: String): WebserverHandle</ID>
<ID>NestedBlockDepth:StartedFlowTransition.kt$StartedFlowTransition$private fun TransitionBuilder.sendToSessionsTransition(sourceSessionIdToMessage: Map&lt;SessionId, SerializedBytes&lt;Any&gt;&gt;)</ID>
<ID>NestedBlockDepth:StatusTransitions.kt$StatusTransitions$ fun verify(tx: LedgerTransaction)</ID> <ID>NestedBlockDepth:StatusTransitions.kt$StatusTransitions$ fun verify(tx: LedgerTransaction)</ID>
<ID>NestedBlockDepth:ThrowableSerializer.kt$ThrowableSerializer$override fun fromProxy(proxy: ThrowableProxy): Throwable</ID> <ID>NestedBlockDepth:ThrowableSerializer.kt$ThrowableSerializer$override fun fromProxy(proxy: ThrowableProxy): Throwable</ID>
<ID>NestedBlockDepth:TransactionVerifierServiceInternal.kt$Verifier$ private fun verifyConstraintsValidity(contractAttachmentsByContract: Map&lt;ContractClassName, ContractAttachment&gt;)</ID> <ID>NestedBlockDepth:TransactionVerifierServiceInternal.kt$Verifier$ private fun verifyConstraintsValidity(contractAttachmentsByContract: Map&lt;ContractClassName, ContractAttachment&gt;)</ID>
@ -1316,7 +1298,6 @@
<ID>SpreadOperator:ConfigUtilities.kt$(*pairs)</ID> <ID>SpreadOperator:ConfigUtilities.kt$(*pairs)</ID>
<ID>SpreadOperator:Configuration.kt$Configuration.Validation.Error$(*(containingPath.toList() + this.containingPath).toTypedArray())</ID> <ID>SpreadOperator:Configuration.kt$Configuration.Validation.Error$(*(containingPath.toList() + this.containingPath).toTypedArray())</ID>
<ID>SpreadOperator:ContractJarTestUtils.kt$ContractJarTestUtils$(jarName, *contractNames.map{ "${it.replace(".", "/")}.class" }.toTypedArray())</ID> <ID>SpreadOperator:ContractJarTestUtils.kt$ContractJarTestUtils$(jarName, *contractNames.map{ "${it.replace(".", "/")}.class" }.toTypedArray())</ID>
<ID>SpreadOperator:CordaCliWrapper.kt$(RunLast().useOut(System.out).useAnsi(defaultAnsiMode), DefaultExceptionHandler&lt;List&lt;Any&gt;&gt;().useErr(System.err).useAnsi(defaultAnsiMode).andExit(ExitCodes.FAILURE), *args)</ID>
<ID>SpreadOperator:CordaRPCOpsImpl.kt$CordaRPCOpsImpl$(logicType, context(), *args)</ID> <ID>SpreadOperator:CordaRPCOpsImpl.kt$CordaRPCOpsImpl$(logicType, context(), *args)</ID>
<ID>SpreadOperator:CordaX500Name.kt$CordaX500Name.Companion$(*Locale.getISOCountries(), unspecifiedCountry)</ID> <ID>SpreadOperator:CordaX500Name.kt$CordaX500Name.Companion$(*Locale.getISOCountries(), unspecifiedCountry)</ID>
<ID>SpreadOperator:CustomCordapp.kt$CustomCordapp$(*classes.map { it.name }.toTypedArray())</ID> <ID>SpreadOperator:CustomCordapp.kt$CustomCordapp$(*classes.map { it.name }.toTypedArray())</ID>
@ -1326,7 +1307,6 @@
<ID>SpreadOperator:DockerInstantiator.kt$DockerInstantiator$(*it.toTypedArray())</ID> <ID>SpreadOperator:DockerInstantiator.kt$DockerInstantiator$(*it.toTypedArray())</ID>
<ID>SpreadOperator:DummyContract.kt$DummyContract.Companion$( /* INPUTS */ *priors.toTypedArray(), /* COMMAND */ Command(cmd, priorState.owner.owningKey), /* OUTPUT */ StateAndContract(state, PROGRAM_ID) )</ID> <ID>SpreadOperator:DummyContract.kt$DummyContract.Companion$( /* INPUTS */ *priors.toTypedArray(), /* COMMAND */ Command(cmd, priorState.owner.owningKey), /* OUTPUT */ StateAndContract(state, PROGRAM_ID) )</ID>
<ID>SpreadOperator:DummyContract.kt$DummyContract.Companion$(*items)</ID> <ID>SpreadOperator:DummyContract.kt$DummyContract.Companion$(*items)</ID>
<ID>SpreadOperator:DummyContractV2.kt$DummyContractV2.Companion$( /* INPUTS */ *priors.toTypedArray(), /* COMMAND */ Command(cmd, priorState.owners.map { it.owningKey }), /* OUTPUT */ StateAndContract(state, DummyContractV2.PROGRAM_ID) )</ID>
<ID>SpreadOperator:ExceptionsErrorCodeFunctions.kt$(*fields)</ID> <ID>SpreadOperator:ExceptionsErrorCodeFunctions.kt$(*fields)</ID>
<ID>SpreadOperator:ExceptionsErrorCodeFunctions.kt$(*fields, cause.staticLocationBasedHash(hashedFields, visited + cause))</ID> <ID>SpreadOperator:ExceptionsErrorCodeFunctions.kt$(*fields, cause.staticLocationBasedHash(hashedFields, visited + cause))</ID>
<ID>SpreadOperator:ExceptionsErrorCodeFunctions.kt$(*hashedFields.invoke(this))</ID> <ID>SpreadOperator:ExceptionsErrorCodeFunctions.kt$(*hashedFields.invoke(this))</ID>
@ -1483,6 +1463,7 @@
<ID>TooGenericExceptionCaught:AbstractNode.kt$ex: Exception</ID> <ID>TooGenericExceptionCaught:AbstractNode.kt$ex: Exception</ID>
<ID>TooGenericExceptionCaught:AbstractNodeTests.kt$ColdJVM.Companion$t: Throwable</ID> <ID>TooGenericExceptionCaught:AbstractNodeTests.kt$ColdJVM.Companion$t: Throwable</ID>
<ID>TooGenericExceptionCaught:Amount.kt$Amount.Companion$e: Exception</ID> <ID>TooGenericExceptionCaught:Amount.kt$Amount.Companion$e: Exception</ID>
<ID>TooGenericExceptionCaught:ArtemisRpcBroker.kt$ArtemisRpcBroker$th: Throwable</ID>
<ID>TooGenericExceptionCaught:AttachmentDemo.kt$e: Exception</ID> <ID>TooGenericExceptionCaught:AttachmentDemo.kt$e: Exception</ID>
<ID>TooGenericExceptionCaught:AttachmentLoadingTests.kt$AttachmentLoadingTests.ConsumeAndBroadcastResponderFlow$e: Exception</ID> <ID>TooGenericExceptionCaught:AttachmentLoadingTests.kt$AttachmentLoadingTests.ConsumeAndBroadcastResponderFlow$e: Exception</ID>
<ID>TooGenericExceptionCaught:AttachmentVersionNumberMigration.kt$AttachmentVersionNumberMigration$e: Exception</ID> <ID>TooGenericExceptionCaught:AttachmentVersionNumberMigration.kt$AttachmentVersionNumberMigration$e: Exception</ID>
@ -1491,7 +1472,6 @@
<ID>TooGenericExceptionCaught:BankOfCordaWebApi.kt$BankOfCordaWebApi$e: Exception</ID> <ID>TooGenericExceptionCaught:BankOfCordaWebApi.kt$BankOfCordaWebApi$e: Exception</ID>
<ID>TooGenericExceptionCaught:BlobInspector.kt$BlobInspector$e: Exception</ID> <ID>TooGenericExceptionCaught:BlobInspector.kt$BlobInspector$e: Exception</ID>
<ID>TooGenericExceptionCaught:BootstrapperView.kt$BootstrapperView$e: Exception</ID> <ID>TooGenericExceptionCaught:BootstrapperView.kt$BootstrapperView$e: Exception</ID>
<ID>TooGenericExceptionCaught:BridgeControlListener.kt$BridgeControlListener$ex: Exception</ID>
<ID>TooGenericExceptionCaught:BrokerJaasLoginModule.kt$BrokerJaasLoginModule$e: Exception</ID> <ID>TooGenericExceptionCaught:BrokerJaasLoginModule.kt$BrokerJaasLoginModule$e: Exception</ID>
<ID>TooGenericExceptionCaught:CertRole.kt$CertRole.Companion$ex: ArrayIndexOutOfBoundsException</ID> <ID>TooGenericExceptionCaught:CertRole.kt$CertRole.Companion$ex: ArrayIndexOutOfBoundsException</ID>
<ID>TooGenericExceptionCaught:CheckpointAgent.kt$CheckpointAgent.Companion$e: Exception</ID> <ID>TooGenericExceptionCaught:CheckpointAgent.kt$CheckpointAgent.Companion$e: Exception</ID>
@ -1506,9 +1486,6 @@
<ID>TooGenericExceptionCaught:ContractUpgradeTransactions.kt$ContractUpgradeWireTransaction$e: Exception</ID> <ID>TooGenericExceptionCaught:ContractUpgradeTransactions.kt$ContractUpgradeWireTransaction$e: Exception</ID>
<ID>TooGenericExceptionCaught:CordaAuthenticationPlugin.kt$CordaAuthenticationPlugin$e: Exception</ID> <ID>TooGenericExceptionCaught:CordaAuthenticationPlugin.kt$CordaAuthenticationPlugin$e: Exception</ID>
<ID>TooGenericExceptionCaught:CordaClassResolver.kt$LoggingWhitelist.Companion$ioEx: Exception</ID> <ID>TooGenericExceptionCaught:CordaClassResolver.kt$LoggingWhitelist.Companion$ioEx: Exception</ID>
<ID>TooGenericExceptionCaught:CordaFutureImpl.kt$CordaFutureImpl$e: Exception</ID>
<ID>TooGenericExceptionCaught:CordaFutureImpl.kt$ValueOrException$e: Exception</ID>
<ID>TooGenericExceptionCaught:CordaFutureImpl.kt$e: Exception</ID>
<ID>TooGenericExceptionCaught:CordaPersistence.kt$CordaPersistence$e: Exception</ID> <ID>TooGenericExceptionCaught:CordaPersistence.kt$CordaPersistence$e: Exception</ID>
<ID>TooGenericExceptionCaught:CordaRPCClientTest.kt$CordaRPCClientTest$e: Exception</ID> <ID>TooGenericExceptionCaught:CordaRPCClientTest.kt$CordaRPCClientTest$e: Exception</ID>
<ID>TooGenericExceptionCaught:CordaRPCOpsImpl.kt$CordaRPCOpsImpl$e: Exception</ID> <ID>TooGenericExceptionCaught:CordaRPCOpsImpl.kt$CordaRPCOpsImpl$e: Exception</ID>
@ -1522,6 +1499,7 @@
<ID>TooGenericExceptionCaught:DeserializeSimpleTypesTests.kt$DeserializeSimpleTypesTests$e: Exception</ID> <ID>TooGenericExceptionCaught:DeserializeSimpleTypesTests.kt$DeserializeSimpleTypesTests$e: Exception</ID>
<ID>TooGenericExceptionCaught:DockerInstantiator.kt$DockerInstantiator$e: Exception</ID> <ID>TooGenericExceptionCaught:DockerInstantiator.kt$DockerInstantiator$e: Exception</ID>
<ID>TooGenericExceptionCaught:DriverDSLImpl.kt$DriverDSLImpl$e: Exception</ID> <ID>TooGenericExceptionCaught:DriverDSLImpl.kt$DriverDSLImpl$e: Exception</ID>
<ID>TooGenericExceptionCaught:DriverDSLImpl.kt$DriverDSLImpl.Companion$th: Throwable</ID>
<ID>TooGenericExceptionCaught:DriverDSLImpl.kt$exception: Throwable</ID> <ID>TooGenericExceptionCaught:DriverDSLImpl.kt$exception: Throwable</ID>
<ID>TooGenericExceptionCaught:DriverTests.kt$DriverTests$e: Exception</ID> <ID>TooGenericExceptionCaught:DriverTests.kt$DriverTests$e: Exception</ID>
<ID>TooGenericExceptionCaught:ErrorCodeLoggingTests.kt$e: Exception</ID> <ID>TooGenericExceptionCaught:ErrorCodeLoggingTests.kt$e: Exception</ID>
@ -1616,7 +1594,7 @@
<ID>TooGenericExceptionCaught:ReconnectingCordaRPCOps.kt$ReconnectingCordaRPCOps.ReconnectingRPCConnection$ex: Exception</ID> <ID>TooGenericExceptionCaught:ReconnectingCordaRPCOps.kt$ReconnectingCordaRPCOps.ReconnectingRPCConnection$ex: Exception</ID>
<ID>TooGenericExceptionCaught:ReconnectingObservable.kt$ReconnectingObservable.ReconnectingSubscriber$e: Exception</ID> <ID>TooGenericExceptionCaught:ReconnectingObservable.kt$ReconnectingObservable.ReconnectingSubscriber$e: Exception</ID>
<ID>TooGenericExceptionCaught:RpcServerObservableSerializerTests.kt$RpcServerObservableSerializerTests$e: Exception</ID> <ID>TooGenericExceptionCaught:RpcServerObservableSerializerTests.kt$RpcServerObservableSerializerTests$e: Exception</ID>
<ID>TooGenericExceptionCaught:SSLHelper.kt$LoggingTrustManagerWrapper$ex: Exception</ID> <ID>TooGenericExceptionCaught:SSLHelper.kt$ex: Exception</ID>
<ID>TooGenericExceptionCaught:ScheduledFlowIntegrationTests.kt$ScheduledFlowIntegrationTests$ex: Exception</ID> <ID>TooGenericExceptionCaught:ScheduledFlowIntegrationTests.kt$ScheduledFlowIntegrationTests$ex: Exception</ID>
<ID>TooGenericExceptionCaught:SerializationOutputTests.kt$SerializationOutputTests$t: Throwable</ID> <ID>TooGenericExceptionCaught:SerializationOutputTests.kt$SerializationOutputTests$t: Throwable</ID>
<ID>TooGenericExceptionCaught:ShutdownManager.kt$ShutdownManager$t: Throwable</ID> <ID>TooGenericExceptionCaught:ShutdownManager.kt$ShutdownManager$t: Throwable</ID>
@ -1644,7 +1622,6 @@
<ID>TooGenericExceptionCaught:ValidatingNotaryFlow.kt$ValidatingNotaryFlow$e: Exception</ID> <ID>TooGenericExceptionCaught:ValidatingNotaryFlow.kt$ValidatingNotaryFlow$e: Exception</ID>
<ID>TooGenericExceptionCaught:VaultStateMigration.kt$VaultStateIterator$e: Exception</ID> <ID>TooGenericExceptionCaught:VaultStateMigration.kt$VaultStateIterator$e: Exception</ID>
<ID>TooGenericExceptionCaught:VaultStateMigration.kt$VaultStateMigration$e: Exception</ID> <ID>TooGenericExceptionCaught:VaultStateMigration.kt$VaultStateMigration$e: Exception</ID>
<ID>TooGenericExceptionCaught:VersionedParsingExampleTest.kt$VersionedParsingExampleTest.RpcSettingsSpec$e: Exception</ID>
<ID>TooGenericExceptionCaught:WebServer.kt$WebServer$e: Exception</ID> <ID>TooGenericExceptionCaught:WebServer.kt$WebServer$e: Exception</ID>
<ID>TooGenericExceptionCaught:WebServer.kt$e: Exception</ID> <ID>TooGenericExceptionCaught:WebServer.kt$e: Exception</ID>
<ID>TooGenericExceptionCaught:WebServer.kt$ex: Exception</ID> <ID>TooGenericExceptionCaught:WebServer.kt$ex: Exception</ID>
@ -1699,7 +1676,6 @@
<ID>TooManyFunctions:CryptoUtils.kt$net.corda.core.crypto.CryptoUtils.kt</ID> <ID>TooManyFunctions:CryptoUtils.kt$net.corda.core.crypto.CryptoUtils.kt</ID>
<ID>TooManyFunctions:Currencies.kt$net.corda.finance.Currencies.kt</ID> <ID>TooManyFunctions:Currencies.kt$net.corda.finance.Currencies.kt</ID>
<ID>TooManyFunctions:Driver.kt$DriverParameters</ID> <ID>TooManyFunctions:Driver.kt$DriverParameters</ID>
<ID>TooManyFunctions:DriverDSLImpl.kt$DriverDSLImpl : InternalDriverDSL</ID>
<ID>TooManyFunctions:EncodingUtils.kt$net.corda.core.utilities.EncodingUtils.kt</ID> <ID>TooManyFunctions:EncodingUtils.kt$net.corda.core.utilities.EncodingUtils.kt</ID>
<ID>TooManyFunctions:FlowLogic.kt$FlowLogic&lt;out T&gt;</ID> <ID>TooManyFunctions:FlowLogic.kt$FlowLogic&lt;out T&gt;</ID>
<ID>TooManyFunctions:FlowStateMachineImpl.kt$FlowStateMachineImpl&lt;R&gt; : FiberFlowStateMachineFlowFiber</ID> <ID>TooManyFunctions:FlowStateMachineImpl.kt$FlowStateMachineImpl&lt;R&gt; : FiberFlowStateMachineFlowFiber</ID>
@ -1761,6 +1737,18 @@
<ID>TopLevelPropertyNaming:SerializationEnvironment.kt$val _inheritableContextSerializationEnv = InheritableThreadLocalToggleField&lt;SerializationEnvironment&gt;("inheritableContextSerializationEnv") { stack -&gt; stack.fold(false) { isAGlobalThreadBeingCreated, e -&gt; isAGlobalThreadBeingCreated || (e.className == "io.netty.util.concurrent.GlobalEventExecutor" &amp;&amp; e.methodName == "startThread") || (e.className == "java.util.concurrent.ForkJoinPool\$DefaultForkJoinWorkerThreadFactory" &amp;&amp; e.methodName == "newThread") } }</ID> <ID>TopLevelPropertyNaming:SerializationEnvironment.kt$val _inheritableContextSerializationEnv = InheritableThreadLocalToggleField&lt;SerializationEnvironment&gt;("inheritableContextSerializationEnv") { stack -&gt; stack.fold(false) { isAGlobalThreadBeingCreated, e -&gt; isAGlobalThreadBeingCreated || (e.className == "io.netty.util.concurrent.GlobalEventExecutor" &amp;&amp; e.methodName == "startThread") || (e.className == "java.util.concurrent.ForkJoinPool\$DefaultForkJoinWorkerThreadFactory" &amp;&amp; e.methodName == "newThread") } }</ID>
<ID>TopLevelPropertyNaming:SerializationEnvironment.kt$val _rpcClientSerializationEnv = SimpleToggleField&lt;SerializationEnvironment&gt;("rpcClientSerializationEnv")</ID> <ID>TopLevelPropertyNaming:SerializationEnvironment.kt$val _rpcClientSerializationEnv = SimpleToggleField&lt;SerializationEnvironment&gt;("rpcClientSerializationEnv")</ID>
<ID>TopLevelPropertyNaming:SerializationFormat.kt$const val encodingNotPermittedFormat = "Encoding not permitted: %s"</ID> <ID>TopLevelPropertyNaming:SerializationFormat.kt$const val encodingNotPermittedFormat = "Encoding not permitted: %s"</ID>
<ID>UnusedImports:Amount.kt$import net.corda.core.crypto.CompositeKey</ID>
<ID>UnusedImports:Amount.kt$import net.corda.core.identity.Party</ID>
<ID>UnusedImports:DummyLinearStateSchemaV1.kt$import net.corda.core.contracts.ContractState</ID>
<ID>UnusedImports:FlowsExecutionModeRpcTest.kt$import net.corda.core.internal.packageName</ID>
<ID>UnusedImports:FlowsExecutionModeRpcTest.kt$import net.corda.finance.schemas.CashSchemaV1</ID>
<ID>UnusedImports:InternalTestUtils.kt$import java.nio.file.Files</ID>
<ID>UnusedImports:InternalTestUtils.kt$import net.corda.nodeapi.internal.loadDevCaTrustStore</ID>
<ID>UnusedImports:NetworkMap.kt$import net.corda.core.node.NodeInfo</ID>
<ID>UnusedImports:NodeParameters.kt$import net.corda.core.identity.Party</ID>
<ID>UnusedImports:SerializerFactory.kt$import java.io.NotSerializableException</ID>
<ID>UnusedImports:TransformTypes.kt$import net.corda.core.serialization.CordaSerializationTransformEnumDefaults</ID>
<ID>UnusedImports:VaultSchema.kt$import net.corda.core.contracts.ContractState</ID>
<ID>VariableNaming:AttachmentsClassLoaderSerializationTests.kt$AttachmentsClassLoaderSerializationTests$val DUMMY_NOTARY = TestIdentity(DUMMY_NOTARY_NAME, 20).party</ID> <ID>VariableNaming:AttachmentsClassLoaderSerializationTests.kt$AttachmentsClassLoaderSerializationTests$val DUMMY_NOTARY = TestIdentity(DUMMY_NOTARY_NAME, 20).party</ID>
<ID>VariableNaming:AttachmentsClassLoaderSerializationTests.kt$AttachmentsClassLoaderSerializationTests$val MEGA_CORP = TestIdentity(CordaX500Name("MegaCorp", "London", "GB")).party</ID> <ID>VariableNaming:AttachmentsClassLoaderSerializationTests.kt$AttachmentsClassLoaderSerializationTests$val MEGA_CORP = TestIdentity(CordaX500Name("MegaCorp", "London", "GB")).party</ID>
<ID>VariableNaming:BootstrapperView.kt$BootstrapperView$val YAML_MAPPER = Constants.getContextMapper()</ID> <ID>VariableNaming:BootstrapperView.kt$BootstrapperView$val YAML_MAPPER = Constants.getContextMapper()</ID>
@ -1885,8 +1873,6 @@
<ID>VariableNaming:VaultQueryTests.kt$VaultQueryTestsBase$// Beware: do not use `MyContractClass::class.qualifiedName` as this returns a fully qualified name using "dot" notation for enclosed class val MYCONTRACT_ID = "net.corda.node.services.vault.VaultQueryTestsBase\$MyContractClass"</ID> <ID>VariableNaming:VaultQueryTests.kt$VaultQueryTestsBase$// Beware: do not use `MyContractClass::class.qualifiedName` as this returns a fully qualified name using "dot" notation for enclosed class val MYCONTRACT_ID = "net.corda.node.services.vault.VaultQueryTestsBase\$MyContractClass"</ID>
<ID>VariableNaming:ZeroCouponBond.kt$ZeroCouponBond$val TEST_TX_TIME_1: Instant get() = Instant.parse("2017-09-02T12:00:00.00Z")</ID> <ID>VariableNaming:ZeroCouponBond.kt$ZeroCouponBond$val TEST_TX_TIME_1: Instant get() = Instant.parse("2017-09-02T12:00:00.00Z")</ID>
<ID>WildcardImport:AMQPClient.kt$import io.netty.channel.*</ID> <ID>WildcardImport:AMQPClient.kt$import io.netty.channel.*</ID>
<ID>WildcardImport:AMQPClientSerializationScheme.kt$import net.corda.serialization.internal.*</ID>
<ID>WildcardImport:AMQPClientSerializationScheme.kt$import net.corda.serialization.internal.amqp.*</ID>
<ID>WildcardImport:AMQPRemoteTypeModel.kt$import net.corda.serialization.internal.model.*</ID> <ID>WildcardImport:AMQPRemoteTypeModel.kt$import net.corda.serialization.internal.model.*</ID>
<ID>WildcardImport:AMQPSerializationScheme.kt$import net.corda.core.serialization.*</ID> <ID>WildcardImport:AMQPSerializationScheme.kt$import net.corda.core.serialization.*</ID>
<ID>WildcardImport:AMQPServerSerializationScheme.kt$import net.corda.serialization.internal.amqp.*</ID> <ID>WildcardImport:AMQPServerSerializationScheme.kt$import net.corda.serialization.internal.amqp.*</ID>
@ -2023,9 +2009,6 @@
<ID>WildcardImport:CordaModule.kt$import net.corda.core.crypto.*</ID> <ID>WildcardImport:CordaModule.kt$import net.corda.core.crypto.*</ID>
<ID>WildcardImport:CordaModule.kt$import net.corda.core.identity.*</ID> <ID>WildcardImport:CordaModule.kt$import net.corda.core.identity.*</ID>
<ID>WildcardImport:CordaModule.kt$import net.corda.core.transactions.*</ID> <ID>WildcardImport:CordaModule.kt$import net.corda.core.transactions.*</ID>
<ID>WildcardImport:CordaRPCClientTest.kt$import net.corda.core.context.*</ID>
<ID>WildcardImport:CordaRPCClientTest.kt$import net.corda.core.messaging.*</ID>
<ID>WildcardImport:CordaRPCClientTest.kt$import net.corda.testing.core.*</ID>
<ID>WildcardImport:CordaRPCOps.kt$import net.corda.core.node.services.vault.*</ID> <ID>WildcardImport:CordaRPCOps.kt$import net.corda.core.node.services.vault.*</ID>
<ID>WildcardImport:CordaRPCOpsImplTest.kt$import net.corda.core.messaging.*</ID> <ID>WildcardImport:CordaRPCOpsImplTest.kt$import net.corda.core.messaging.*</ID>
<ID>WildcardImport:CordaRPCOpsImplTest.kt$import org.assertj.core.api.Assertions.*</ID> <ID>WildcardImport:CordaRPCOpsImplTest.kt$import org.assertj.core.api.Assertions.*</ID>
@ -2056,8 +2039,6 @@
<ID>WildcardImport:DBTransactionStorage.kt$import net.corda.core.serialization.*</ID> <ID>WildcardImport:DBTransactionStorage.kt$import net.corda.core.serialization.*</ID>
<ID>WildcardImport:DBTransactionStorage.kt$import net.corda.nodeapi.internal.persistence.*</ID> <ID>WildcardImport:DBTransactionStorage.kt$import net.corda.nodeapi.internal.persistence.*</ID>
<ID>WildcardImport:DBTransactionStorageTests.kt$import net.corda.testing.core.*</ID> <ID>WildcardImport:DBTransactionStorageTests.kt$import net.corda.testing.core.*</ID>
<ID>WildcardImport:DefaultKryoCustomizer.kt$import de.javakaffee.kryoserializers.guava.*</ID>
<ID>WildcardImport:DefaultKryoCustomizer.kt$import net.corda.core.transactions.*</ID>
<ID>WildcardImport:DeleteForDJVM.kt$import kotlin.annotation.AnnotationTarget.*</ID> <ID>WildcardImport:DeleteForDJVM.kt$import kotlin.annotation.AnnotationTarget.*</ID>
<ID>WildcardImport:DemoBench.kt$import tornadofx.*</ID> <ID>WildcardImport:DemoBench.kt$import tornadofx.*</ID>
<ID>WildcardImport:DemoBenchNodeInfoFilesCopier.kt$import tornadofx.*</ID> <ID>WildcardImport:DemoBenchNodeInfoFilesCopier.kt$import tornadofx.*</ID>
@ -2121,8 +2102,6 @@
<ID>WildcardImport:FlowStateMachineImpl.kt$import net.corda.core.flows.*</ID> <ID>WildcardImport:FlowStateMachineImpl.kt$import net.corda.core.flows.*</ID>
<ID>WildcardImport:FlowStateMachineImpl.kt$import net.corda.core.internal.*</ID> <ID>WildcardImport:FlowStateMachineImpl.kt$import net.corda.core.internal.*</ID>
<ID>WildcardImport:FlowsDrainingModeContentionTest.kt$import net.corda.core.flows.*</ID> <ID>WildcardImport:FlowsDrainingModeContentionTest.kt$import net.corda.core.flows.*</ID>
<ID>WildcardImport:FxTransactionBuildTutorial.kt$import net.corda.core.contracts.*</ID>
<ID>WildcardImport:FxTransactionBuildTutorial.kt$import net.corda.core.flows.*</ID>
<ID>WildcardImport:FxTransactionBuildTutorialTest.kt$import net.corda.finance.*</ID> <ID>WildcardImport:FxTransactionBuildTutorialTest.kt$import net.corda.finance.*</ID>
<ID>WildcardImport:GenericsTests.kt$import net.corda.serialization.internal.amqp.testutils.*</ID> <ID>WildcardImport:GenericsTests.kt$import net.corda.serialization.internal.amqp.testutils.*</ID>
<ID>WildcardImport:Gui.kt$import tornadofx.*</ID> <ID>WildcardImport:Gui.kt$import tornadofx.*</ID>
@ -2168,10 +2147,7 @@
<ID>WildcardImport:InternalMockNetwork.kt$import net.corda.core.internal.*</ID> <ID>WildcardImport:InternalMockNetwork.kt$import net.corda.core.internal.*</ID>
<ID>WildcardImport:InternalMockNetwork.kt$import net.corda.node.services.config.*</ID> <ID>WildcardImport:InternalMockNetwork.kt$import net.corda.node.services.config.*</ID>
<ID>WildcardImport:InternalMockNetwork.kt$import net.corda.testing.node.*</ID> <ID>WildcardImport:InternalMockNetwork.kt$import net.corda.testing.node.*</ID>
<ID>WildcardImport:InternalSerializationTestHelpers.kt$import net.corda.serialization.internal.*</ID>
<ID>WildcardImport:InternalTestUtils.kt$import net.corda.core.contracts.*</ID> <ID>WildcardImport:InternalTestUtils.kt$import net.corda.core.contracts.*</ID>
<ID>WildcardImport:InternalUtils.kt$import java.security.cert.*</ID>
<ID>WildcardImport:InternalUtils.kt$import net.corda.core.crypto.*</ID>
<ID>WildcardImport:IssuerModel.kt$import tornadofx.*</ID> <ID>WildcardImport:IssuerModel.kt$import tornadofx.*</ID>
<ID>WildcardImport:JVMConfig.kt$import tornadofx.*</ID> <ID>WildcardImport:JVMConfig.kt$import tornadofx.*</ID>
<ID>WildcardImport:JacksonSupport.kt$import com.fasterxml.jackson.core.*</ID> <ID>WildcardImport:JacksonSupport.kt$import com.fasterxml.jackson.core.*</ID>
@ -2194,8 +2170,6 @@
<ID>WildcardImport:KotlinIntegrationTestingTutorial.kt$import net.corda.testing.core.*</ID> <ID>WildcardImport:KotlinIntegrationTestingTutorial.kt$import net.corda.testing.core.*</ID>
<ID>WildcardImport:Kryo.kt$import com.esotericsoftware.kryo.*</ID> <ID>WildcardImport:Kryo.kt$import com.esotericsoftware.kryo.*</ID>
<ID>WildcardImport:Kryo.kt$import net.corda.core.transactions.*</ID> <ID>WildcardImport:Kryo.kt$import net.corda.core.transactions.*</ID>
<ID>WildcardImport:KryoCheckpointSerializer.kt$import net.corda.core.serialization.*</ID>
<ID>WildcardImport:KryoCheckpointSerializer.kt$import net.corda.serialization.internal.*</ID>
<ID>WildcardImport:KryoStreamsTest.kt$import java.io.*</ID> <ID>WildcardImport:KryoStreamsTest.kt$import java.io.*</ID>
<ID>WildcardImport:KryoTests.kt$import kotlin.test.*</ID> <ID>WildcardImport:KryoTests.kt$import kotlin.test.*</ID>
<ID>WildcardImport:KryoTests.kt$import net.corda.core.crypto.*</ID> <ID>WildcardImport:KryoTests.kt$import net.corda.core.crypto.*</ID>
@ -2249,19 +2223,12 @@
<ID>WildcardImport:NetworkBootstrapper.kt$import net.corda.nodeapi.internal.*</ID> <ID>WildcardImport:NetworkBootstrapper.kt$import net.corda.nodeapi.internal.*</ID>
<ID>WildcardImport:NetworkBootstrapperRunnerTests.kt$import org.junit.*</ID> <ID>WildcardImport:NetworkBootstrapperRunnerTests.kt$import org.junit.*</ID>
<ID>WildcardImport:NetworkBootstrapperTest.kt$import net.corda.core.internal.*</ID> <ID>WildcardImport:NetworkBootstrapperTest.kt$import net.corda.core.internal.*</ID>
<ID>WildcardImport:NetworkBootstrapperTest.kt$import net.corda.testing.core.*</ID>
<ID>WildcardImport:NetworkBuilder.kt$import net.corda.networkbuilder.nodes.*</ID> <ID>WildcardImport:NetworkBuilder.kt$import net.corda.networkbuilder.nodes.*</ID>
<ID>WildcardImport:NetworkIdentityModel.kt$import net.corda.client.jfx.utils.*</ID> <ID>WildcardImport:NetworkIdentityModel.kt$import net.corda.client.jfx.utils.*</ID>
<ID>WildcardImport:NetworkMapServer.kt$import javax.ws.rs.*</ID> <ID>WildcardImport:NetworkMapServer.kt$import javax.ws.rs.*</ID>
<ID>WildcardImport:NetworkMapTest.kt$import net.corda.core.internal.*</ID> <ID>WildcardImport:NetworkMapTest.kt$import net.corda.core.internal.*</ID>
<ID>WildcardImport:NetworkMapTest.kt$import net.corda.testing.core.*</ID> <ID>WildcardImport:NetworkMapTest.kt$import net.corda.testing.core.*</ID>
<ID>WildcardImport:NetworkMapTest.kt$import net.corda.testing.node.internal.*</ID> <ID>WildcardImport:NetworkMapTest.kt$import net.corda.testing.node.internal.*</ID>
<ID>WildcardImport:NetworkMapUpdater.kt$import net.corda.core.internal.*</ID>
<ID>WildcardImport:NetworkMapUpdater.kt$import net.corda.nodeapi.internal.network.*</ID>
<ID>WildcardImport:NetworkMapUpdaterTest.kt$import com.nhaarman.mockito_kotlin.*</ID>
<ID>WildcardImport:NetworkMapUpdaterTest.kt$import net.corda.core.internal.*</ID>
<ID>WildcardImport:NetworkMapUpdaterTest.kt$import net.corda.testing.core.*</ID>
<ID>WildcardImport:NetworkMapUpdaterTest.kt$import org.junit.*</ID>
<ID>WildcardImport:NetworkParametersReader.kt$import net.corda.core.internal.*</ID> <ID>WildcardImport:NetworkParametersReader.kt$import net.corda.core.internal.*</ID>
<ID>WildcardImport:NetworkParametersReaderTest.kt$import net.corda.core.internal.*</ID> <ID>WildcardImport:NetworkParametersReaderTest.kt$import net.corda.core.internal.*</ID>
<ID>WildcardImport:NetworkParametersReaderTest.kt$import net.corda.nodeapi.internal.network.*</ID> <ID>WildcardImport:NetworkParametersReaderTest.kt$import net.corda.nodeapi.internal.network.*</ID>
@ -2362,7 +2329,6 @@
<ID>WildcardImport:PathUtils.kt$import java.io.*</ID> <ID>WildcardImport:PathUtils.kt$import java.io.*</ID>
<ID>WildcardImport:PathUtils.kt$import java.nio.file.*</ID> <ID>WildcardImport:PathUtils.kt$import java.nio.file.*</ID>
<ID>WildcardImport:PersistentIdentityMigrationNewTableTest.kt$import net.corda.testing.core.*</ID> <ID>WildcardImport:PersistentIdentityMigrationNewTableTest.kt$import net.corda.testing.core.*</ID>
<ID>WildcardImport:PersistentIdentityServiceTests.kt$import net.corda.testing.core.*</ID>
<ID>WildcardImport:PersistentNetworkMapCacheTest.kt$import net.corda.testing.core.*</ID> <ID>WildcardImport:PersistentNetworkMapCacheTest.kt$import net.corda.testing.core.*</ID>
<ID>WildcardImport:PersistentStateServiceTests.kt$import net.corda.core.contracts.*</ID> <ID>WildcardImport:PersistentStateServiceTests.kt$import net.corda.core.contracts.*</ID>
<ID>WildcardImport:Portfolio.kt$import net.corda.core.contracts.*</ID> <ID>WildcardImport:Portfolio.kt$import net.corda.core.contracts.*</ID>
@ -2387,8 +2353,6 @@
<ID>WildcardImport:QueryCriteriaUtils.kt$import net.corda.core.node.services.vault.LikenessOperator.*</ID> <ID>WildcardImport:QueryCriteriaUtils.kt$import net.corda.core.node.services.vault.LikenessOperator.*</ID>
<ID>WildcardImport:RPCMultipleInterfacesTests.kt$import org.junit.Assert.*</ID> <ID>WildcardImport:RPCMultipleInterfacesTests.kt$import org.junit.Assert.*</ID>
<ID>WildcardImport:RPCSecurityManagerImpl.kt$import org.apache.shiro.authc.*</ID> <ID>WildcardImport:RPCSecurityManagerImpl.kt$import org.apache.shiro.authc.*</ID>
<ID>WildcardImport:RPCServer.kt$import net.corda.core.utilities.*</ID>
<ID>WildcardImport:RPCServer.kt$import org.apache.activemq.artemis.api.core.client.*</ID>
<ID>WildcardImport:ReceiveFinalityFlowTest.kt$import net.corda.node.services.statemachine.StaffedFlowHospital.*</ID> <ID>WildcardImport:ReceiveFinalityFlowTest.kt$import net.corda.node.services.statemachine.StaffedFlowHospital.*</ID>
<ID>WildcardImport:ReceiveFinalityFlowTest.kt$import net.corda.testing.node.internal.*</ID> <ID>WildcardImport:ReceiveFinalityFlowTest.kt$import net.corda.testing.node.internal.*</ID>
<ID>WildcardImport:ReceiveTransactionFlow.kt$import net.corda.core.contracts.*</ID> <ID>WildcardImport:ReceiveTransactionFlow.kt$import net.corda.core.contracts.*</ID>
@ -2427,7 +2391,6 @@
<ID>WildcardImport:SearchField.kt$import tornadofx.*</ID> <ID>WildcardImport:SearchField.kt$import tornadofx.*</ID>
<ID>WildcardImport:SecureHashTest.kt$import org.junit.Assert.*</ID> <ID>WildcardImport:SecureHashTest.kt$import org.junit.Assert.*</ID>
<ID>WildcardImport:SendTransactionFlow.kt$import net.corda.core.internal.*</ID> <ID>WildcardImport:SendTransactionFlow.kt$import net.corda.core.internal.*</ID>
<ID>WildcardImport:SerializationEnvironmentRule.kt$import net.corda.testing.internal.*</ID>
<ID>WildcardImport:SerializationHelper.kt$import java.lang.reflect.*</ID> <ID>WildcardImport:SerializationHelper.kt$import java.lang.reflect.*</ID>
<ID>WildcardImport:SerializationHelper.kt$import net.corda.core.serialization.*</ID> <ID>WildcardImport:SerializationHelper.kt$import net.corda.core.serialization.*</ID>
<ID>WildcardImport:SerializationOutputTests.kt$import java.time.*</ID> <ID>WildcardImport:SerializationOutputTests.kt$import java.time.*</ID>
@ -2572,7 +2535,6 @@
<ID>WildcardImport:VaultWithCashTest.kt$import net.corda.testing.core.*</ID> <ID>WildcardImport:VaultWithCashTest.kt$import net.corda.testing.core.*</ID>
<ID>WildcardImport:VaultWithCashTest.kt$import net.corda.testing.internal.vault.*</ID> <ID>WildcardImport:VaultWithCashTest.kt$import net.corda.testing.internal.vault.*</ID>
<ID>WildcardImport:VerifyTransactionTest.kt$import net.corda.finance.contracts.asset.Cash.Commands.*</ID> <ID>WildcardImport:VerifyTransactionTest.kt$import net.corda.finance.contracts.asset.Cash.Commands.*</ID>
<ID>WildcardImport:VersionedParsingExampleTest.kt$import net.corda.common.configuration.parsing.internal.*</ID>
<ID>WildcardImport:WebServerController.kt$import tornadofx.*</ID> <ID>WildcardImport:WebServerController.kt$import tornadofx.*</ID>
<ID>WildcardImport:WhitelistBasedTypeModelConfiguration.kt$import org.apache.qpid.proton.amqp.*</ID> <ID>WildcardImport:WhitelistBasedTypeModelConfiguration.kt$import org.apache.qpid.proton.amqp.*</ID>
<ID>WildcardImport:WhitelistGenerator.kt$import net.corda.core.internal.*</ID> <ID>WildcardImport:WhitelistGenerator.kt$import net.corda.core.internal.*</ID>
@ -2581,8 +2543,6 @@
<ID>WildcardImport:WireTransaction.kt$import net.corda.core.internal.*</ID> <ID>WildcardImport:WireTransaction.kt$import net.corda.core.internal.*</ID>
<ID>WildcardImport:WithFinality.kt$import net.corda.core.flows.*</ID> <ID>WildcardImport:WithFinality.kt$import net.corda.core.flows.*</ID>
<ID>WildcardImport:WithMockNet.kt$import com.natpryce.hamkrest.*</ID> <ID>WildcardImport:WithMockNet.kt$import com.natpryce.hamkrest.*</ID>
<ID>WildcardImport:WorkflowTransactionBuildTutorial.kt$import net.corda.core.contracts.*</ID>
<ID>WildcardImport:WorkflowTransactionBuildTutorial.kt$import net.corda.core.flows.*</ID>
<ID>WildcardImport:X509CRLSerializer.kt$import net.corda.serialization.internal.amqp.*</ID> <ID>WildcardImport:X509CRLSerializer.kt$import net.corda.serialization.internal.amqp.*</ID>
<ID>WildcardImport:X509CertificateSerializer.kt$import net.corda.serialization.internal.amqp.*</ID> <ID>WildcardImport:X509CertificateSerializer.kt$import net.corda.serialization.internal.amqp.*</ID>
<ID>WildcardImport:X509EdDSAEngine.kt$import java.security.*</ID> <ID>WildcardImport:X509EdDSAEngine.kt$import java.security.*</ID>

View File

@ -194,3 +194,5 @@ style:
active: true active: true
excludes: "**/buildSrc/**" excludes: "**/buildSrc/**"
excludeImports: 'java.util.*,kotlinx.android.synthetic.*' excludeImports: 'java.util.*,kotlinx.android.synthetic.*'
UnusedImports:
active: true

View File

@ -0,0 +1,62 @@
FROM azul/zulu-openjdk:8u192
## Add packages, clean cache, create dirs, create corda user and change ownership
RUN apt-get update && \
apt-get -y upgrade && \
apt-get -y install bash curl unzip netstat lsof telnet netcat && \
rm -rf /var/lib/apt/lists/* && \
mkdir -p /opt/corda/cordapps && \
mkdir -p /opt/corda/persistence && \
mkdir -p /opt/corda/certificates && \
mkdir -p /opt/corda/drivers && \
mkdir -p /opt/corda/logs && \
mkdir -p /opt/corda/bin && \
mkdir -p /opt/corda/additional-node-infos && \
mkdir -p /etc/corda && \
addgroup corda && \
useradd corda -g corda -m -d /opt/corda && \
chown -R corda:corda /opt/corda && \
chown -R corda:corda /etc/corda
ENV CORDAPPS_FOLDER="/opt/corda/cordapps" \
PERSISTENCE_FOLDER="/opt/corda/persistence" \
CERTIFICATES_FOLDER="/opt/corda/certificates" \
DRIVERS_FOLDER="/opt/corda/drivers" \
CONFIG_FOLDER="/etc/corda" \
MY_P2P_PORT=10200 \
MY_RPC_PORT=10201 \
MY_RPC_ADMIN_PORT=10202 \
PATH=$PATH:/opt/corda/bin \
JVM_ARGS="-XX:+UseG1GC -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap " \
CORDA_ARGS=""
##CORDAPPS FOLDER
VOLUME ["/opt/corda/cordapps"]
##PERSISTENCE FOLDER
VOLUME ["/opt/corda/persistence"]
##CERTS FOLDER
VOLUME ["/opt/corda/certificates"]
##OPTIONAL JDBC DRIVERS FOLDER
VOLUME ["/opt/corda/drivers"]
##LOG FOLDER
VOLUME ["/opt/corda/logs"]
##ADDITIONAL NODE INFOS FOLDER
VOLUME ["/opt/corda/additional-node-infos"]
##CONFIG LOCATION
VOLUME ["/etc/corda"]
##CORDA JAR
COPY --chown=corda:corda corda.jar /opt/corda/bin/corda.jar
##CONFIG MANIPULATOR JAR
COPY --chown=corda:corda config-exporter.jar /opt/corda/config-exporter.jar
##CONFIG GENERATOR SHELL SCRIPT
COPY --chown=corda:corda generate-config.sh /opt/corda/bin/config-generator
##CORDA RUN SCRIPT
COPY --chown=corda:corda run-corda.sh /opt/corda/bin/run-corda
##BASE CONFIG FOR GENERATOR
COPY --chown=corda:corda starting-node.conf /opt/corda/starting-node.conf
USER "corda"
EXPOSE ${MY_P2P_PORT} ${MY_RPC_PORT} ${MY_RPC_ADMIN_PORT}
WORKDIR /opt/corda
CMD ["run-corda"]

View File

@ -0,0 +1,66 @@
FROM amazonlinux:2
## Add packages, clean cache, create dirs, create corda user and change ownership
RUN amazon-linux-extras enable corretto8 && \
yum -y install java-1.8.0-amazon-corretto-devel && \
yum -y install bash && \
yum -y install curl && \
yum -y install unzip && \
yum -y install lsof telnet net-tools nmap-ncat && \
yum clean all && \
rm -rf /var/cache/yum && \
mkdir -p /opt/corda/cordapps && \
mkdir -p /opt/corda/persistence && \
mkdir -p /opt/corda/certificates && \
mkdir -p /opt/corda/drivers && \
mkdir -p /opt/corda/logs && \
mkdir -p /opt/corda/bin && \
mkdir -p /opt/corda/additional-node-infos && \
mkdir -p /etc/corda && \
groupadd corda && \
useradd corda -g corda -m -d /opt/corda && \
chown -R corda:corda /opt/corda && \
chown -R corda:corda /etc/corda
ENV CORDAPPS_FOLDER="/opt/corda/cordapps" \
PERSISTENCE_FOLDER="/opt/corda/persistence" \
CERTIFICATES_FOLDER="/opt/corda/certificates" \
DRIVERS_FOLDER="/opt/corda/drivers" \
CONFIG_FOLDER="/etc/corda" \
MY_P2P_PORT=10200 \
MY_RPC_PORT=10201 \
MY_RPC_ADMIN_PORT=10202 \
PATH=$PATH:/opt/corda/bin \
JVM_ARGS="-XX:+UseG1GC -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap " \
CORDA_ARGS=""
##CORDAPPS FOLDER
VOLUME ["/opt/corda/cordapps"]
##PERSISTENCE FOLDER
VOLUME ["/opt/corda/persistence"]
##CERTS FOLDER
VOLUME ["/opt/corda/certificates"]
##OPTIONAL JDBC DRIVERS FOLDER
VOLUME ["/opt/corda/drivers"]
##LOG FOLDER
VOLUME ["/opt/corda/logs"]
##ADDITIONAL NODE INFOS FOLDER
VOLUME ["/opt/corda/additional-node-infos"]
##CONFIG LOCATION
VOLUME ["/etc/corda"]
##CORDA JAR
COPY --chown=corda:corda corda.jar /opt/corda/bin/corda.jar
##CONFIG MANIPULATOR JAR
COPY --chown=corda:corda config-exporter.jar /opt/corda/config-exporter.jar
##CONFIG GENERATOR SHELL SCRIPT
COPY --chown=corda:corda generate-config.sh /opt/corda/bin/config-generator
##CORDA RUN SCRIPT
COPY --chown=corda:corda run-corda.sh /opt/corda/bin/run-corda
##BASE CONFIG FOR GENERATOR
COPY --chown=corda:corda starting-node.conf /opt/corda/starting-node.conf
USER "corda"
EXPOSE ${MY_P2P_PORT} ${MY_RPC_PORT} ${MY_RPC_ADMIN_PORT}
WORKDIR /opt/corda
CMD ["run-corda"]

View File

@ -11,7 +11,6 @@ import net.corda.common.validation.internal.Validated
import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.NodeConfiguration
import net.corda.node.services.config.parseAsNodeConfiguration import net.corda.node.services.config.parseAsNodeConfiguration
import net.corda.nodeapi.internal.config.toConfig import net.corda.nodeapi.internal.config.toConfig
import net.corda.nodeapi.internal.config.toConfigValue
import java.io.File import java.io.File
class ConfigExporter { class ConfigExporter {

View File

@ -1,197 +0,0 @@
# Makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = build
# User-friendly check for sphinx-build
ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
endif
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
HTMLSPHINXOPTS = $(ALLSPHINXOPTS) -t htmlmode
PDFSPHINXOPTS = $(ALLSPHINXOPTS) -t pdfmode
# the i18n builder cannot share the environment and doctrees with the others
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage gettext
help:
@echo "Please use \`make <target>' where <target> is one of"
@echo " html to make standalone HTML files"
@echo " dirhtml to make HTML files named index.html in directories"
@echo " singlehtml to make a single large HTML file"
@echo " pickle to make pickle files"
@echo " json to make JSON files"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " qthelp to make HTML files and a qthelp project"
@echo " applehelp to make an Apple Help Book"
@echo " devhelp to make HTML files and a Devhelp project"
@echo " epub to make an epub"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " latexpdf to make LaTeX files and run them through pdflatex"
@echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
@echo " text to make text files"
@echo " man to make manual pages"
@echo " texinfo to make Texinfo files"
@echo " info to make Texinfo files and run them through makeinfo"
@echo " gettext to make PO message catalogs"
@echo " changes to make an overview of all changed/added/deprecated items"
@echo " xml to make Docutils-native XML files"
@echo " pseudoxml to make pseudoxml-XML files for display purposes"
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
@echo " coverage to run coverage check of the documentation (if enabled)"
clean:
rm -rf $(BUILDDIR)/*
html:
$(SPHINXBUILD) -b html $(HTMLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
dirhtml:
$(SPHINXBUILD) -b dirhtml $(HTMLSPHINXOPTS) $(BUILDDIR)/dirhtml
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
singlehtml:
$(SPHINXBUILD) -b singlehtml $(HTMLSPHINXOPTS) $(BUILDDIR)/singlehtml
@echo
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
pickle:
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
@echo
@echo "Build finished; now you can process the pickle files."
json:
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
@echo
@echo "Build finished; now you can process the JSON files."
htmlhelp:
$(SPHINXBUILD) -b htmlhelp $(HTMLSPHINXOPTS) $(BUILDDIR)/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
".hhp project file in $(BUILDDIR)/htmlhelp."
qthelp:
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
@echo
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Playground.qhcp"
@echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Playground.qhc"
applehelp:
$(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp
@echo
@echo "Build finished. The help book is in $(BUILDDIR)/applehelp."
@echo "N.B. You won't be able to view it unless you put it in" \
"~/Library/Documentation/Help or install it in your application" \
"bundle."
devhelp:
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
@echo
@echo "Build finished."
@echo "To view the help file:"
@echo "# mkdir -p $$HOME/.local/share/devhelp/Playground"
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Playground"
@echo "# devhelp"
epub:
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
@echo
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
latex:
$(SPHINXBUILD) -b latex $(PDFSPHINXOPTS) $(BUILDDIR)/latex
@echo
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
@echo "Run \`make' in that directory to run these through (pdf)latex" \
"(use \`make latexpdf' here to do that automatically)."
latexpdf:
$(SPHINXBUILD) -b latex $(PDFSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through pdflatex..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
latexpdfja:
$(SPHINXBUILD) -b latex $(PDFSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through platex and dvipdfmx..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
text:
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
@echo
@echo "Build finished. The text files are in $(BUILDDIR)/text."
man:
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
@echo
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
texinfo:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
@echo "Run \`make' in that directory to run these through makeinfo" \
"(use \`make info' here to do that automatically)."
info:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo "Running Texinfo files through makeinfo..."
make -C $(BUILDDIR)/texinfo info
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
gettext:
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
@echo
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
changes:
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
@echo
@echo "The overview file is in $(BUILDDIR)/changes."
linkcheck:
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in $(BUILDDIR)/linkcheck/output.txt."
doctest:
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
@echo "Testing of doctests in the sources finished, look at the " \
"results in $(BUILDDIR)/doctest/output.txt."
coverage:
$(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage
@echo "Testing of coverage in the sources finished, look at the " \
"results in $(BUILDDIR)/coverage/python.txt."
xml:
$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
@echo
@echo "Build finished. The XML files are in $(BUILDDIR)/xml."
pseudoxml:
$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
@echo
@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
pdf:
$(SPHINXBUILD) -b pdf $(PDFSPHINXOPTS) $(BUILDDIR)/pdf

View File

@ -1,41 +1,23 @@
# Corda Documentation Build # Docs
This Readme describes how to build the Corda documentation for the current version. The output html files will be written to the `corda\docs\build\html` directory. ## Released documentation
## Prerequisites / First time build All released Corda documentation has now been moved to a standalone public documentation repository where the doc source can be found:
Before you begin, you need to: [corda/corda-docs](https://github.com/corda/corda-docs)
1. Install Docker.
1. Ensure that Docker is running.
1. Select **Expose daemon on tcp://localhost:2375 without TLS** in the Docker Settings (which you can open from the **System Tray** by right-clicking the **Docker symbol** and then selecting **Settings**)
## Build process See the [readme](https://github.com/corda/corda-docs/blob/master/README.md) and [usage docs](https://github.com/corda/corda-docs/tree/master/usage-docs) pages for instructions on how to use the new repo and build the docs locally.
1. Open a cmd dialogue.
1. Navigate to the root location (this is the `\corda` directory)
1. Run the documentation build (`gradlew makeDocs` or `./gradlew makeDocs`)
**Windows users:** *If this task fails because Docker can't find make-docsite.sh, go to Settings > Shared Drives in the Docker system tray You can contribute to the docs source as before via fork & PR. We now use `markdown` to write/edit (instead of `rst`) and `Hugo` to build (instead of `Sphinx`).
agent, make sure the relevant drive is shared, and click 'Reset credentials'.*
# RST style guide The published documentation is available at https://docs.corda.net.
The Corda documentation is described using the ReStructured Text (RST) markup language. For details of the syntax, see [this](http://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html). ## Documentation for future releases
# Version placeholders R3's technical writing team, R3 engineering, and other R3 teams use a separate, private docs repo for working on draft documentation content targeting future releases:
We currently support the following placeholders; they get substituted with the correct value at build time: [corda/corda-docs-develop](https://github.com/corda/corda-docs-develop)
```groovy These docs are published as part of each quarterly release of Corda. At that point their doc source becomes available and open for contributions in the [public docs repo](https://github.com/corda/corda-docs).
"|corda_version|"
"|corda_version_lower|"
"|java_version|"
"|kotlin_version|"
"|gradle_plugins_version|"
"|quasar_version|"
```
If you put one of these in an rst file anywhere (including in a code tag), it will be substituted with the value from `constants.properties` The new documentation process is described in the technical writing team's space on [R3's internal confluence wiki](https://r3-cev.atlassian.net/wiki/spaces/EN/pages/1701249087/Technical+Writing).
(which is in the root of the project) at build time. `corda_version_lower` returns the current Corda version in lowercase which is useful
for case sensitive artifacts such as docker images.
The code for this can be found near the top of the conf.py file in the `docs/source` directory.

View File

@ -1,80 +0,0 @@
import org.apache.tools.ant.taskdefs.condition.Os
import java.nio.file.Files
apply plugin: 'org.jetbrains.dokka'
apply plugin: 'kotlin'
dependencies {
compile rootProject
}
def internalPackagePrefixes(sourceDirs) {
def prefixes = []
// Kotlin allows packages to deviate from the directory structure, but let's assume they don't:
sourceDirs.collect { sourceDir ->
sourceDir.traverse(type: groovy.io.FileType.DIRECTORIES) {
if (it.name == 'internal') {
prefixes.add sourceDir.toPath().relativize(it.toPath()).toString().replace(File.separator, '.')
}
}
}
prefixes
}
ext {
// TODO: Add '../client/jfx/src/main/kotlin' and '../client/mock/src/main/kotlin' if we decide to make them into public API
dokkaSourceDirs = files('../core/src/main/kotlin', '../client/rpc/src/main/kotlin', '../finance/workflows/src/main/kotlin', '../finance/contracts/src/main/kotlin', '../client/jackson/src/main/kotlin',
'../testing/test-utils/src/main/kotlin', '../testing/node-driver/src/main/kotlin')
internalPackagePrefixes = internalPackagePrefixes(dokkaSourceDirs)
}
dokka {
outputDirectory = file("${rootProject.rootDir}/docs/build/html/api/kotlin")
}
task dokkaJavadoc(type: org.jetbrains.dokka.gradle.DokkaTask) {
outputFormat = "javadoc"
outputDirectory = file("${rootProject.rootDir}/docs/build/html/api/javadoc")
}
[dokka, dokkaJavadoc].collect {
it.configure {
moduleName = 'corda'
processConfigurations = ['compile']
sourceDirs = dokkaSourceDirs
includes = ['packages.md']
jdkVersion = 8
externalDocumentationLink {
url = new URL("http://fasterxml.github.io/jackson-core/javadoc/2.9/")
}
externalDocumentationLink {
url = new URL("https://docs.oracle.com/javafx/2/api/")
}
externalDocumentationLink {
url = new URL("http://www.bouncycastle.org/docs/docs1.5on/")
}
internalPackagePrefixes.collect { packagePrefix ->
packageOptions {
prefix = packagePrefix
suppress = true
}
}
}
}
task buildDocs(dependsOn: ['apidocs', 'makeDocs'])
task apidocs(dependsOn: ['dokka', 'dokkaJavadoc'])
task makeDocs(type: Exec) {
// 2 volumes are mounted:
// - the docs project to /opt/docs_builder, where docs building is executed
// - the rest of the projects in /opt, so that code references to other projects are valid
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
commandLine "docker", "run", "--rm", "-v", "${project.projectDir}:/opt/docs_builder", "-v", "${project.projectDir}/..:/opt", "corda/docs-builder:latest", "bash", "-c", "make-docsite.sh"
} else {
commandLine "bash", "-c", "docker run --rm --user \$(id -u):\$(id -g) -v ${project.projectDir}:/opt/docs_builder -v ${project.projectDir}/..:/opt corda/docs-builder:latest bash -c make-docsite.sh"
}
}
apidocs.shouldRunAfter makeDocs

View File

@ -1,11 +0,0 @@
FROM python:2-stretch
RUN apt-get update \
&& apt-get --no-install-recommends install -y texlive preview-latex-style texlive-generic-extra texlive-latex-extra latexmk dos2unix \
&& apt-get -y clean \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
ENV PATH="/opt/docs_builder:${PATH}"
WORKDIR /opt/docs_builder
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt

View File

@ -1 +0,0 @@
Pygments*.whl

View File

@ -1,13 +0,0 @@
FROM python:2-stretch
RUN apt-get update \
&& apt-get --no-install-recommends install -y texlive preview-latex-style texlive-generic-extra texlive-latex-extra latexmk dos2unix \
&& apt-get -y clean \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
ENV PATH="/opt/docs_builder:${PATH}"
WORKDIR /opt/docs_builder
COPY requirements.txt requirements.txt
COPY docs_builder/lexer-fix/Pygments*.whl .
RUN pip install -r requirements.txt
RUN pip install Pygments*.whl --force-reinstall

File diff suppressed because it is too large Load Diff

View File

@ -1,35 +0,0 @@
# Pygments lexer
We were getting a lot of warnings in the docs build, and people were unable to see the real warnings due to this. So we're on a mission
to sort all of these out.
A lot of the errors were because the kotlin lexer in Pygments (the syntax highlighter that sphinx uses) didn't cope with a lot of the
kotlin syntax that we use.
We have fixes for the kotlin lexer that we are trying to get checked into Pygments, but while this is taking place we need to maintain a
slightly hacked corda/docs-build docker image in which to build the docs.
## Some notes on building and testing
The sphinx/pygments brigade have delightfully decided that mercurial is a good idea. So broadly speaking, to build/test a fix:
* checkout pygments from [here](https://bitbucket.org/birkenfeld/pygments-main/overview)
* copy the two python files in (might be worth diffing - they're based on 2.3.1 - nb the kotlin test is entirely new)
* build pygments whl file
```
cd /path/to/pygments/
python setup.py install
pip install wheel
wheel convert dist/Pygments-2.3.1.dev20190 # obviously use your version
cp Pygments-2.3.1.dev20190401-py27-none-any.whl /path/to/corda/docs/source/docs_builder/lexer-fix
```
* build the latest docker build (see docs readme)
```
cd docs
docker build -t corda/docs-builder:latest -f docs_builder/lexer-fix/Dockerfile .
```
* push the new image up to docker hub (nb you can also test by going to /opt/docs and running `./make-docsite.sh`)

View File

@ -1,131 +0,0 @@
# -*- coding: utf-8 -*-
"""
Basic JavaLexer Test
~~~~~~~~~~~~~~~~~~~~
:copyright: Copyright 2006-2017 by the Pygments team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import unittest
from pygments.token import Text, Name, Operator, Keyword, Number, Punctuation, String
from pygments.lexers import KotlinLexer
class KotlinTest(unittest.TestCase):
def setUp(self):
self.lexer = KotlinLexer()
self.maxDiff = None
def testCanCopeWithBackTickNamesInFunctions(self):
fragment = u'fun `wo bble`'
tokens = [
(Keyword, u'fun'),
(Text, u' '),
(Name.Function, u'`wo bble`'),
(Text, u'\n')
]
self.assertEqual(tokens, list(self.lexer.get_tokens(fragment)))
def testCanCopeWithCommasAndDashesInBackTickNames(self):
fragment = u'fun `wo,-bble`'
tokens = [
(Keyword, u'fun'),
(Text, u' '),
(Name.Function, u'`wo,-bble`'),
(Text, u'\n')
]
self.assertEqual(tokens, list(self.lexer.get_tokens(fragment)))
def testCanCopeWithDestructuring(self):
fragment = u'val (a, b) = '
tokens = [
(Keyword, u'val'),
(Text, u' '),
(Punctuation, u'('),
(Name.Property, u'a'),
(Punctuation, u','),
(Text, u' '),
(Name.Property, u'b'),
(Punctuation, u')'),
(Text, u' '),
(Punctuation, u'='),
(Text, u' '),
(Text, u'\n')
]
self.assertEqual(tokens, list(self.lexer.get_tokens(fragment)))
def testCanCopeGenericsInDestructuring(self):
fragment = u'val (a: List<Something>, b: Set<Wobble>) ='
tokens = [
(Keyword, u'val'),
(Text, u' '),
(Punctuation, u'('),
(Name.Property, u'a'),
(Punctuation, u':'),
(Text, u' '),
(Name.Property, u'List'),
(Punctuation, u'<'),
(Name, u'Something'),
(Punctuation, u'>'),
(Punctuation, u','),
(Text, u' '),
(Name.Property, u'b'),
(Punctuation, u':'),
(Text, u' '),
(Name.Property, u'Set'),
(Punctuation, u'<'),
(Name, u'Wobble'),
(Punctuation, u'>'),
(Punctuation, u')'),
(Text, u' '),
(Punctuation, u'='),
(Text, u'\n')
]
self.assertEqual(tokens, list(self.lexer.get_tokens(fragment)))
def testCanCopeWithGenerics(self):
fragment = u'inline fun <reified T : ContractState> VaultService.queryBy(): Vault.Page<T> {'
tokens = [
(Keyword, u'inline fun'),
(Text, u' '),
(Punctuation, u'<'),
(Keyword, u'reified'),
(Text, u' '),
(Name, u'T'),
(Text, u' '),
(Punctuation, u':'),
(Text, u' '),
(Name, u'ContractState'),
(Punctuation, u'>'),
(Text, u' '),
(Name.Class, u'VaultService'),
(Punctuation, u'.'),
(Name.Function, u'queryBy'),
(Punctuation, u'('),
(Punctuation, u')'),
(Punctuation, u':'),
(Text, u' '),
(Name, u'Vault'),
(Punctuation, u'.'),
(Name, u'Page'),
(Punctuation, u'<'),
(Name, u'T'),
(Punctuation, u'>'),
(Text, u' '),
(Punctuation, u'{'),
(Text, u'\n')
]
self.assertEqual(tokens, list(self.lexer.get_tokens(fragment)))
def testShouldCopeWithMultilineComments(self):
fragment = u'"""\nthis\nis\na\ncomment"""'
tokens = [
(String, u'"""\nthis\nis\na\ncomment"""'),
(Text, u'\n')
]
self.assertEqual(tokens, list(self.lexer.get_tokens(fragment)))
if __name__ == '__main__':
unittest.main()

Some files were not shown because too many files have changed in this diff Show More