mirror of
https://github.com/corda/corda.git
synced 2024-12-18 20:47:57 +00:00
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:
commit
adbe030a2c
83
.ci/dev/compatibility/JenkinsfileJDK11Azul
Normal file
83
.ci/dev/compatibility/JenkinsfileJDK11Azul
Normal 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 */
|
||||
}
|
||||
}
|
||||
}
|
2
.ci/dev/integration/Jenkinsfile
vendored
2
.ci/dev/integration/Jenkinsfile
vendored
@ -5,7 +5,7 @@ import static com.r3.build.BuildControl.killAllExistingBuildsForJob
|
||||
killAllExistingBuildsForJob(env.JOB_NAME, env.BUILD_NUMBER.toInteger())
|
||||
|
||||
pipeline {
|
||||
agent { label 'local-k8s' }
|
||||
agent { label 'k8s' }
|
||||
options {
|
||||
timestamps()
|
||||
timeout(time: 3, unit: 'HOURS')
|
||||
|
2
.ci/dev/nightly-regression/Jenkinsfile
vendored
2
.ci/dev/nightly-regression/Jenkinsfile
vendored
@ -4,7 +4,7 @@ import static com.r3.build.BuildControl.killAllExistingBuildsForJob
|
||||
killAllExistingBuildsForJob(env.JOB_NAME, env.BUILD_NUMBER.toInteger())
|
||||
|
||||
pipeline {
|
||||
agent { label 'local-k8s' }
|
||||
agent { label 'k8s' }
|
||||
options {
|
||||
timestamps()
|
||||
overrideIndexTriggers(false)
|
||||
|
2
.ci/dev/on-demand-tests/Jenkinsfile
vendored
2
.ci/dev/on-demand-tests/Jenkinsfile
vendored
@ -3,4 +3,4 @@ import static com.r3.build.BuildControl.killAllExistingBuildsForJob
|
||||
|
||||
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')
|
||||
|
32
.ci/dev/regression/Jenkinsfile
vendored
32
.ci/dev/regression/Jenkinsfile
vendored
@ -4,7 +4,7 @@ import static com.r3.build.BuildControl.killAllExistingBuildsForJob
|
||||
killAllExistingBuildsForJob(env.JOB_NAME, env.BUILD_NUMBER.toInteger())
|
||||
|
||||
pipeline {
|
||||
agent { label 'local-k8s' }
|
||||
agent { label 'k8s' }
|
||||
options {
|
||||
timestamps()
|
||||
buildDiscarder(logRotator(daysToKeepStr: '7', artifactDaysToKeepStr: '7'))
|
||||
@ -61,6 +61,36 @@ pipeline {
|
||||
" 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.')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
2
.ci/dev/smoke/Jenkinsfile
vendored
2
.ci/dev/smoke/Jenkinsfile
vendored
@ -4,7 +4,7 @@ import static com.r3.build.BuildControl.killAllExistingBuildsForJob
|
||||
killAllExistingBuildsForJob(env.JOB_NAME, env.BUILD_NUMBER.toInteger())
|
||||
|
||||
pipeline {
|
||||
agent { label 'local-k8s' }
|
||||
agent { label 'k8s' }
|
||||
options {
|
||||
timestamps()
|
||||
overrideIndexTriggers(false)
|
||||
|
2
.ci/dev/unit/Jenkinsfile
vendored
2
.ci/dev/unit/Jenkinsfile
vendored
@ -5,7 +5,7 @@ import static com.r3.build.BuildControl.killAllExistingBuildsForJob
|
||||
killAllExistingBuildsForJob(env.JOB_NAME, env.BUILD_NUMBER.toInteger())
|
||||
|
||||
pipeline {
|
||||
agent { label 'local-k8s' }
|
||||
agent { label 'k8s' }
|
||||
options {
|
||||
timestamps()
|
||||
timeout(time: 3, unit: 'HOURS')
|
||||
|
19
.github/workflows/jira_assign_issue.yml
vendored
Normal file
19
.github/workflows/jira_assign_issue.yml
vendored
Normal 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
20
.github/workflows/jira_close_issue.yml
vendored
Normal 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
36
.github/workflows/jira_create_issue.yml
vendored
Normal 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
2
Jenkinsfile
vendored
@ -5,7 +5,7 @@ import static com.r3.build.BuildControl.killAllExistingBuildsForJob
|
||||
killAllExistingBuildsForJob(env.JOB_NAME, env.BUILD_NUMBER.toInteger())
|
||||
|
||||
pipeline {
|
||||
agent { label 'local-k8s' }
|
||||
agent { label 'k8s' }
|
||||
options {
|
||||
timestamps()
|
||||
timeout(time: 3, unit: 'HOURS')
|
||||
|
@ -2,7 +2,7 @@
|
||||
<img src="https://www.corda.net/wp-content/themes/corda/assets/images/crda-logo-big.svg" alt="Corda" width="500">
|
||||
</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
|
||||
|
||||
|
120
build.gradle
120
build.gradle
@ -79,8 +79,8 @@ buildscript {
|
||||
ext.djvm_version = constants.getProperty("djvmVersion")
|
||||
ext.deterministic_rt_version = constants.getProperty('deterministicRtVersion')
|
||||
ext.okhttp_version = '3.14.2'
|
||||
ext.netty_version = '4.1.29.Final'
|
||||
ext.tcnative_version = '2.0.14.Final'
|
||||
ext.netty_version = '4.1.46.Final'
|
||||
ext.tcnative_version = '2.0.29.Final'
|
||||
ext.typesafe_config_version = constants.getProperty("typesafeConfigVersion")
|
||||
ext.fileupload_version = '1.4'
|
||||
ext.kryo_version = '4.0.2'
|
||||
@ -187,8 +187,9 @@ buildscript {
|
||||
// See https://github.com/corda/gradle-capsule-plugin
|
||||
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.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 "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.8"
|
||||
}
|
||||
}
|
||||
|
||||
@ -241,6 +242,7 @@ allprojects {
|
||||
apply plugin: 'jacoco'
|
||||
apply plugin: 'org.owasp.dependencycheck'
|
||||
apply plugin: 'kotlin-allopen'
|
||||
apply plugin: 'org.sonarqube'
|
||||
|
||||
allOpen {
|
||||
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
|
||||
// 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()
|
||||
})
|
||||
}
|
||||
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) {
|
||||
@ -635,67 +669,83 @@ buildScan {
|
||||
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) {
|
||||
dependsOn dependxiesModule
|
||||
podLogLevel PodLogLevel.INFO
|
||||
testGroups "integrationTest"
|
||||
numberOfShards 10
|
||||
streamOutput false
|
||||
coresPerFork 2
|
||||
memoryInGbPerFork 12
|
||||
numberOfShards generalPurpose.numberOfShards
|
||||
streamOutput generalPurpose.streamOutput
|
||||
coresPerFork generalPurpose.coresPerFork
|
||||
memoryInGbPerFork generalPurpose.memoryInGbPerFork
|
||||
nodeTaints generalPurpose.nodeTaints
|
||||
distribute DistributeTestsBy.METHOD
|
||||
nodeTaints "big"
|
||||
}
|
||||
task allParallelUnitTest(type: ParallelTestGroup) {
|
||||
dependsOn dependxiesModule
|
||||
podLogLevel PodLogLevel.INFO
|
||||
testGroups "test"
|
||||
numberOfShards 10
|
||||
streamOutput false
|
||||
coresPerFork 2
|
||||
memoryInGbPerFork 12
|
||||
numberOfShards generalPurpose.numberOfShards
|
||||
streamOutput generalPurpose.streamOutput
|
||||
coresPerFork generalPurpose.coresPerFork
|
||||
memoryInGbPerFork generalPurpose.memoryInGbPerFork
|
||||
nodeTaints generalPurpose.nodeTaints
|
||||
distribute DistributeTestsBy.CLASS
|
||||
nodeTaints "small"
|
||||
}
|
||||
task allParallelUnitAndIntegrationTest(type: ParallelTestGroup) {
|
||||
dependsOn dependxiesModule
|
||||
testGroups "test", "integrationTest"
|
||||
numberOfShards 15
|
||||
streamOutput false
|
||||
coresPerFork 6
|
||||
memoryInGbPerFork 10
|
||||
numberOfShards generalPurpose.numberOfShards
|
||||
streamOutput generalPurpose.streamOutput
|
||||
coresPerFork generalPurpose.coresPerFork
|
||||
memoryInGbPerFork generalPurpose.memoryInGbPerFork
|
||||
nodeTaints generalPurpose.nodeTaints
|
||||
distribute DistributeTestsBy.METHOD
|
||||
nodeTaints "big"
|
||||
}
|
||||
task parallelRegressionTest(type: ParallelTestGroup) {
|
||||
testGroups "test", "integrationTest", "smokeTest"
|
||||
dependsOn dependxiesModule
|
||||
numberOfShards 15
|
||||
streamOutput false
|
||||
coresPerFork 2
|
||||
memoryInGbPerFork 10
|
||||
numberOfShards generalPurpose.numberOfShards
|
||||
streamOutput generalPurpose.streamOutput
|
||||
coresPerFork generalPurpose.coresPerFork
|
||||
memoryInGbPerFork generalPurpose.memoryInGbPerFork
|
||||
nodeTaints generalPurpose.nodeTaints
|
||||
distribute DistributeTestsBy.METHOD
|
||||
nodeTaints "big"
|
||||
}
|
||||
task allParallelSmokeTest(type: ParallelTestGroup) {
|
||||
testGroups "smokeTest"
|
||||
dependsOn dependxiesModule
|
||||
numberOfShards 4
|
||||
streamOutput false
|
||||
coresPerFork 6
|
||||
memoryInGbPerFork 10
|
||||
distribute DistributeTestsBy.CLASS
|
||||
nodeTaints "big"
|
||||
numberOfShards generalPurpose.numberOfShards
|
||||
streamOutput generalPurpose.streamOutput
|
||||
coresPerFork generalPurpose.coresPerFork
|
||||
memoryInGbPerFork generalPurpose.memoryInGbPerFork
|
||||
nodeTaints generalPurpose.nodeTaints
|
||||
distribute DistributeTestsBy.METHOD
|
||||
}
|
||||
task allParallelSlowIntegrationTest(type: ParallelTestGroup) {
|
||||
testGroups "slowIntegrationTest"
|
||||
dependsOn dependxiesModule
|
||||
numberOfShards 4
|
||||
streamOutput false
|
||||
coresPerFork 6
|
||||
memoryInGbPerFork 10
|
||||
distribute DistributeTestsBy.CLASS
|
||||
nodeTaints "big"
|
||||
numberOfShards generalPurpose.numberOfShards
|
||||
streamOutput generalPurpose.streamOutput
|
||||
coresPerFork generalPurpose.coresPerFork
|
||||
memoryInGbPerFork generalPurpose.memoryInGbPerFork
|
||||
nodeTaints generalPurpose.nodeTaints
|
||||
distribute DistributeTestsBy.METHOD
|
||||
}
|
||||
apply plugin: 'com.r3.testing.distributed-testing'
|
||||
apply plugin: 'com.r3.testing.image-building'
|
||||
|
@ -1,7 +1,6 @@
|
||||
package net.corda.client.rpc
|
||||
|
||||
import co.paralleluniverse.fibers.Suspendable
|
||||
import com.esotericsoftware.kryo.KryoException
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.flows.StartableByRPC
|
||||
import net.corda.core.messaging.startFlow
|
||||
|
@ -14,13 +14,13 @@ import net.corda.core.utilities.Try
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.core.utilities.millis
|
||||
import net.corda.core.utilities.seconds
|
||||
import net.corda.coretesting.internal.testThreadFactory
|
||||
import net.corda.node.services.rpc.RPCServerConfiguration
|
||||
import net.corda.nodeapi.RPCApi
|
||||
import net.corda.testing.common.internal.eventually
|
||||
import net.corda.testing.common.internal.succeeds
|
||||
import net.corda.testing.core.SerializationEnvironmentRule
|
||||
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.RpcBrokerHandle
|
||||
import net.corda.testing.node.internal.RpcServerHandle
|
||||
@ -75,7 +75,7 @@ class RPCStabilityTests {
|
||||
values.poll()
|
||||
}
|
||||
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
|
||||
} else {
|
||||
null
|
||||
|
@ -21,7 +21,7 @@ object Configuration {
|
||||
/**
|
||||
* 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 {
|
||||
@ -36,10 +36,11 @@ object Configuration {
|
||||
*
|
||||
* @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.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)
|
||||
fun valueIn(configuration: Config): TYPE
|
||||
fun valueIn(configuration: Config, options: Options): TYPE
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @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)
|
||||
fun valueInOrNull(configuration: Config): TYPE? {
|
||||
fun valueInOrNull(configuration: Config, options: Options): TYPE? {
|
||||
|
||||
return when {
|
||||
isSpecifiedBy(configuration) -> valueIn(configuration)
|
||||
isSpecifiedBy(configuration) -> valueIn(configuration, options)
|
||||
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> {
|
||||
|
||||
/**
|
||||
* 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> {
|
||||
|
||||
/**
|
||||
* 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)
|
||||
|
||||
/**
|
||||
@ -181,9 +178,8 @@ object Configuration {
|
||||
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> {
|
||||
|
||||
return validate(configuration, options).mapValid { config -> valid(valueIn(config)) }
|
||||
override fun parse(configuration: Config, options: Configuration.Options): Validated<TYPE, Validation.Error> {
|
||||
return validate(configuration, options).mapValid { config -> valid(valueIn(config, options)) }
|
||||
}
|
||||
|
||||
companion object {
|
||||
@ -199,7 +195,6 @@ object Configuration {
|
||||
* 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 ->
|
||||
|
||||
try {
|
||||
valid(Math.toIntExact(value))
|
||||
} catch (e: ArithmeticException) {
|
||||
@ -210,18 +205,17 @@ object Configuration {
|
||||
/**
|
||||
* 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].
|
||||
*/
|
||||
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].
|
||||
*/
|
||||
fun float(key: String, sensitive: Boolean = false): Standard<Float> = double(key, sensitive).mapValid { value ->
|
||||
|
||||
val floatValue = value.toFloat()
|
||||
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."))
|
||||
@ -233,24 +227,43 @@ object Configuration {
|
||||
/**
|
||||
* 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].
|
||||
*/
|
||||
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].
|
||||
* 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].
|
||||
* 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<*>>
|
||||
|
||||
/**
|
||||
* 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
|
||||
override fun describe(configuration: Config, serialiseValue: (Any?) -> ConfigValue, options: Configuration.Options): ConfigValue
|
||||
|
||||
companion object {
|
||||
|
||||
@ -368,35 +376,35 @@ object Configuration {
|
||||
|
||||
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].
|
||||
*/
|
||||
protected abstract fun parseValid(configuration: Config): Valid<VALUE>
|
||||
protected abstract fun parseValid(configuration: Config, options: Options): Valid<VALUE>
|
||||
}
|
||||
|
||||
/**
|
||||
* Validation and processing options.
|
||||
* @property strict whether to raise unknown property keys as errors.
|
||||
*/
|
||||
class Options(val strict: Boolean = false) {
|
||||
|
||||
companion object {
|
||||
|
||||
/**
|
||||
* Default [Config] options, without [strict] parsing enabled.
|
||||
*/
|
||||
val defaults: Configuration.Options = Options()
|
||||
}
|
||||
}
|
||||
|
||||
object Validation {
|
||||
|
||||
/**
|
||||
* [Config] validation options.
|
||||
* @property strict whether to raise unknown property keys as errors.
|
||||
*/
|
||||
data class Options(val strict: Boolean) {
|
||||
|
||||
companion object {
|
||||
|
||||
/**
|
||||
* Default [Config] validation options, without [strict] parsing enabled.
|
||||
*/
|
||||
val defaults: Configuration.Validation.Options = Options(strict = false)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
|
||||
@ -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].
|
||||
*/
|
||||
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>
|
||||
}
|
@ -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.valid
|
||||
|
||||
internal class LongProperty(key: String, sensitive: Boolean = false) : StandardProperty<Long>(key, Long::class.javaObjectType.simpleName, Config::getLong, Config::getLongList, sensitive) {
|
||||
|
||||
override fun validate(target: Config, options: Configuration.Validation.Options): Valid<Config> {
|
||||
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.Options): Valid<Config> {
|
||||
val validated = super.validate(target, options)
|
||||
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))
|
||||
@ -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
|
||||
|
||||
@ -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 describe(configuration: Config, serialiseValue: (Any?) -> ConfigValue): ConfigValue {
|
||||
|
||||
override fun describe(configuration: Config, serialiseValue: (Any?) -> ConfigValue, options: Configuration.Options): ConfigValue {
|
||||
if (isSensitive) {
|
||||
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 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>()
|
||||
errors += errorsWhenExtractingValue(target)
|
||||
errors += errorsWhenExtractingValue(target, options)
|
||||
if (errors.isEmpty()) {
|
||||
schema?.let { nestedSchema ->
|
||||
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 fun valueIn(configuration: Config): List<TYPE> = delegate.extractListValue.invoke(configuration, key)
|
||||
|
||||
override fun validate(target: Config, options: Configuration.Validation.Options): Valid<Config> {
|
||||
override fun valueIn(configuration: Config, options: Configuration.Options): List<TYPE> = delegate.extractListValue.invoke(configuration, key, options)
|
||||
|
||||
override fun validate(target: Config, options: Configuration.Options): Valid<Config> {
|
||||
val errors = mutableSetOf<Configuration.Validation.Error>()
|
||||
errors += errorsWhenExtractingValue(target)
|
||||
errors += errorsWhenExtractingValue(target, options)
|
||||
if (errors.isEmpty()) {
|
||||
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)
|
||||
@ -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 describe(configuration: Config, serialiseValue: (Any?) -> ConfigValue): ConfigValue {
|
||||
|
||||
override fun describe(configuration: Config, serialiseValue: (Any?) -> ConfigValue, options: Configuration.Options): ConfigValue {
|
||||
if (isSensitive) {
|
||||
return valueDescription(Configuration.Property.Definition.SENSITIVE_DATA_PLACEHOLDER, serialiseValue)
|
||||
}
|
||||
return when {
|
||||
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)
|
||||
}
|
||||
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 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})"
|
||||
|
||||
@ -123,29 +126,31 @@ private class FunctionalProperty<TYPE, MAPPED>(delegate: Configuration.Property.
|
||||
|
||||
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>()
|
||||
errors += delegate.validate(target, options).errors
|
||||
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)
|
||||
}
|
||||
|
||||
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> {
|
||||
|
||||
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 validate(target: Config, options: Configuration.Validation.Options): Valid<Config> {
|
||||
override fun valueIn(configuration: Config, options: Configuration.Options): List<TYPE> = delegate.extractListValue.invoke(configuration, key, options).asSequence()
|
||||
.map { configObject(key to ConfigValueFactory.fromAnyRef(it)) }
|
||||
.map(ConfigObject::toConfig)
|
||||
.map { delegate.valueIn(it, options) }
|
||||
.toList()
|
||||
|
||||
override fun validate(target: Config, options: Configuration.Options): Valid<Config> {
|
||||
val list = try {
|
||||
delegate.extractListValue.invoke(target, key)
|
||||
delegate.extractListValue.invoke(target, key, options)
|
||||
} catch (e: ConfigException) {
|
||||
if (isErrorExpected(e)) {
|
||||
return invalid(e.toValidationError(key, typeName))
|
||||
@ -153,7 +158,11 @@ private class FunctionalListProperty<RAW, TYPE>(delegate: FunctionalProperty<RAW
|
||||
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)
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
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)
|
||||
@ -187,18 +195,16 @@ private class OptionalDelegatedProperty<TYPE>(private val delegate: Configuratio
|
||||
|
||||
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 valueIn(configuration: Config): TYPE? {
|
||||
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, options: Configuration.Options): TYPE? {
|
||||
return when {
|
||||
isSpecifiedBy(configuration) -> delegate.valueIn(configuration)
|
||||
isSpecifiedBy(configuration) -> delegate.valueIn(configuration, options)
|
||||
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 errors = result.errors
|
||||
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> {
|
||||
|
||||
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 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>()
|
||||
errors += delegate.validate(target, options).errors
|
||||
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)
|
||||
}
|
||||
@ -248,7 +253,6 @@ private class ListMappingProperty<TYPE, MAPPED>(private val delegate: Configurat
|
||||
}
|
||||
|
||||
fun ConfigException.toValidationError(keyName: String? = null, typeName: String): Configuration.Validation.Error {
|
||||
|
||||
val toError = when (this) {
|
||||
is ConfigException.Missing -> Configuration.Validation.Error.MissingValue.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())
|
||||
}
|
||||
|
||||
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 {
|
||||
valueIn(target)
|
||||
valueIn(target, options)
|
||||
return emptySet()
|
||||
} catch (exception: ConfigException) {
|
||||
if (isErrorExpected(exception)) {
|
||||
|
@ -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 ->
|
||||
property.validate(target, options).errors
|
||||
@ -47,9 +47,9 @@ internal class Schema(override val name: String?, unorderedProperties: Iterable<
|
||||
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 {
|
||||
|
@ -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)
|
||||
|
||||
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) }
|
||||
|
||||
@ -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)
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
@ -6,21 +6,17 @@ 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 {
|
||||
|
||||
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 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)
|
||||
}
|
||||
|
||||
private class Spec(key: String, versionDefaultValue: Int, prefix: String?) : Configuration.Specification<Int>("Version", prefix) {
|
||||
|
||||
private val version by int(key = key).optional().withDefaultValue(versionDefaultValue)
|
||||
|
||||
override fun parseValid(configuration: Config) = valid(version.valueIn(configuration))
|
||||
override fun parseValid(configuration: Config, options: Configuration.Options) = valid(version.valueIn(configuration, options))
|
||||
}
|
||||
}
|
@ -21,8 +21,8 @@ class PropertyTest {
|
||||
assertThat(property.key).isEqualTo(key)
|
||||
assertThat(property.isMandatory).isTrue()
|
||||
assertThat(property.isSpecifiedBy(configuration)).isTrue()
|
||||
assertThat(property.valueIn(configuration)).isEqualTo(value)
|
||||
assertThat(configuration[property]).isEqualTo(value)
|
||||
assertThat(property.valueIn(configuration, Configuration.Options.defaults)).isEqualTo(value)
|
||||
assertThat(configuration.withOptions(Configuration.Options.defaults)[property]).isEqualTo(value)
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
@ -38,7 +38,7 @@ class PropertyTest {
|
||||
assertThat(property.key).isEqualTo(key)
|
||||
assertThat(property.isMandatory).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)
|
||||
@ -54,7 +54,7 @@ class PropertyTest {
|
||||
assertThat(property.key).isEqualTo(key)
|
||||
assertThat(property.isMandatory).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)
|
||||
@ -70,7 +70,7 @@ class PropertyTest {
|
||||
assertThat(property.key).isEqualTo(key)
|
||||
assertThat(property.isMandatory).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)
|
||||
@ -85,7 +85,7 @@ class PropertyTest {
|
||||
assertThat(property.key).isEqualTo(key)
|
||||
assertThat(property.isMandatory).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)
|
||||
@ -101,7 +101,7 @@ class PropertyTest {
|
||||
assertThat(property.key).isEqualTo(key)
|
||||
assertThat(property.isMandatory).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)
|
||||
@ -116,7 +116,7 @@ class PropertyTest {
|
||||
assertThat(property.key).isEqualTo(key)
|
||||
assertThat(property.isMandatory).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)
|
||||
@ -132,7 +132,7 @@ class PropertyTest {
|
||||
assertThat(property.key).isEqualTo(key)
|
||||
assertThat(property.isMandatory).isFalse()
|
||||
assertThat(property.isSpecifiedBy(configuration)).isTrue()
|
||||
assertThat(property.valueIn(configuration)).isEqualTo(value)
|
||||
assertThat(property.valueIn(configuration, Configuration.Options.defaults)).isEqualTo(value)
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
@ -147,7 +147,7 @@ class PropertyTest {
|
||||
assertThat(property.key).isEqualTo(key)
|
||||
assertThat(property.isMandatory).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.isMandatory).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)
|
||||
@ -179,7 +179,7 @@ class PropertyTest {
|
||||
assertThat(property.key).isEqualTo(key)
|
||||
assertThat(property.isMandatory).isTrue()
|
||||
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)
|
||||
@ -195,7 +195,7 @@ class PropertyTest {
|
||||
assertThat(property.key).isEqualTo(key)
|
||||
assertThat(property.isMandatory).isFalse()
|
||||
assertThat(property.isSpecifiedBy(configuration)).isTrue()
|
||||
assertThat(property.valueIn(configuration)).isEqualTo(value)
|
||||
assertThat(property.valueIn(configuration, Configuration.Options.defaults)).isEqualTo(value)
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
@ -211,7 +211,7 @@ class PropertyTest {
|
||||
assertThat(property.key).isEqualTo(key)
|
||||
assertThat(property.isMandatory).isFalse()
|
||||
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)
|
||||
@ -226,7 +226,7 @@ class PropertyTest {
|
||||
assertThat(property.key).isEqualTo(key)
|
||||
assertThat(property.isMandatory).isFalse()
|
||||
assertThat(property.isSpecifiedBy(configuration)).isFalse()
|
||||
assertThat(property.valueIn(configuration)).isNull()
|
||||
assertThat(property.valueIn(configuration, Configuration.Options.defaults)).isNull()
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
@ -242,6 +242,6 @@ class PropertyTest {
|
||||
assertThat(property.key).isEqualTo(key)
|
||||
assertThat(property.isMandatory).isFalse()
|
||||
assertThat(property.isSpecifiedBy(configuration)).isFalse()
|
||||
assertThat(property.valueIn(configuration)).isEqualTo(defaultValue)
|
||||
assertThat(property.valueIn(configuration, Configuration.Options.defaults)).isEqualTo(defaultValue)
|
||||
}
|
||||
}
|
@ -15,7 +15,7 @@ class PropertyValidationTest {
|
||||
|
||||
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.first()).isInstanceOfSatisfying(Configuration.Validation.Error.MissingValue::class.java) { error ->
|
||||
@ -34,7 +34,7 @@ class PropertyValidationTest {
|
||||
|
||||
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.first()).isInstanceOfSatisfying(Configuration.Validation.Error.MissingValue::class.java) { error ->
|
||||
@ -53,7 +53,7 @@ class PropertyValidationTest {
|
||||
|
||||
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.first()).isInstanceOfSatisfying(Configuration.Validation.Error.MissingValue::class.java) { error ->
|
||||
@ -72,7 +72,7 @@ class PropertyValidationTest {
|
||||
|
||||
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.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)
|
||||
|
||||
assertThat(property.validate(configuration).errors).isEmpty()
|
||||
assertThat(property.validate(configuration, Configuration.Options.defaults).errors).isEmpty()
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
@ -114,7 +114,7 @@ class PropertyValidationTest {
|
||||
|
||||
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.first()).isInstanceOfSatisfying(Configuration.Validation.Error.BadValue::class.java) { error ->
|
||||
@ -134,7 +134,7 @@ class PropertyValidationTest {
|
||||
|
||||
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.first()).isInstanceOfSatisfying(Configuration.Validation.Error.WrongType::class.java) { error ->
|
||||
@ -154,7 +154,7 @@ class PropertyValidationTest {
|
||||
|
||||
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.first()).isInstanceOfSatisfying(Configuration.Validation.Error.WrongType::class.java) { error ->
|
||||
@ -174,7 +174,7 @@ class PropertyValidationTest {
|
||||
|
||||
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)
|
||||
@ -186,7 +186,7 @@ class PropertyValidationTest {
|
||||
|
||||
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.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()
|
||||
|
||||
assertThat(property.validate(configuration).errors).satisfies { errors ->
|
||||
assertThat(property.validate(configuration, Configuration.Options.defaults).errors).satisfies { errors ->
|
||||
|
||||
assertThat(errors).hasSize(1)
|
||||
assertThat(errors.first()).isInstanceOfSatisfying(Configuration.Validation.Error.WrongType::class.java) { error ->
|
||||
@ -226,7 +226,7 @@ class PropertyValidationTest {
|
||||
|
||||
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.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()
|
||||
|
||||
assertThat(property.validate(configuration).errors).satisfies { errors ->
|
||||
assertThat(property.validate(configuration, Configuration.Options.defaults).errors).satisfies { errors ->
|
||||
|
||||
assertThat(errors).hasSize(1)
|
||||
assertThat(errors.first()).isInstanceOfSatisfying(Configuration.Validation.Error.WrongType::class.java) { error ->
|
||||
@ -272,7 +272,7 @@ class PropertyValidationTest {
|
||||
|
||||
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.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()
|
||||
|
||||
assertThat(property.validate(configuration).errors).satisfies { errors ->
|
||||
assertThat(property.validate(configuration, Configuration.Options.defaults).errors).satisfies { errors ->
|
||||
|
||||
assertThat(errors).hasSize(1)
|
||||
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()
|
||||
|
||||
assertThat(property.validate(configuration).isValid).isTrue()
|
||||
assertThat(property.validate(configuration, Configuration.Options.defaults).isValid).isTrue()
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
@ -333,7 +333,7 @@ class PropertyValidationTest {
|
||||
|
||||
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)
|
||||
@ -350,7 +350,7 @@ class PropertyValidationTest {
|
||||
|
||||
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 ->
|
||||
|
||||
|
@ -29,7 +29,7 @@ class SchemaTest {
|
||||
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 result = barConfigSchema.validate(configuration)
|
||||
val result = barConfigSchema.validate(configuration, Configuration.Options.defaults)
|
||||
println(barConfigSchema.description())
|
||||
|
||||
assertThat(result.isValid).isTrue()
|
||||
@ -59,17 +59,17 @@ class SchemaTest {
|
||||
val fooConfigSchema = Configuration.Schema.withProperties { setOf(boolean("prop4"), double("prop5")) }
|
||||
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.filter { error -> error.keyName == "prop4" }).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()
|
||||
|
||||
val errorsWithDefaultOptions = barConfigSchema.validate(configuration).errors
|
||||
val errorsWithDefaultOptions = barConfigSchema.validate(configuration, Configuration.Options.defaults).errors
|
||||
|
||||
assertThat(errorsWithDefaultOptions).isEmpty()
|
||||
}
|
||||
@ -98,7 +98,7 @@ class SchemaTest {
|
||||
val fooConfigSchema = Configuration.Schema.withProperties { setOf(boolean("prop4"), double("prop5")) }
|
||||
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()
|
||||
}
|
||||
@ -127,7 +127,7 @@ class SchemaTest {
|
||||
val fooConfigSchema = Configuration.Schema.withProperties { setOf(boolean("prop4"), double("prop5")) }
|
||||
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)
|
||||
|
||||
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 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) }
|
||||
|
||||
@ -185,7 +185,7 @@ class SchemaTest {
|
||||
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 printedConfiguration = barConfigSchema.describe(configuration)
|
||||
val printedConfiguration = barConfigSchema.describe(configuration, options = Configuration.Options.defaults)
|
||||
|
||||
val description = printedConfiguration.serialize().also { println(it) }
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
package net.corda.common.configuration.parsing.internal
|
||||
|
||||
import com.typesafe.config.Config
|
||||
import net.corda.common.validation.internal.Validated
|
||||
import net.corda.common.validation.internal.Validated.Companion.invalid
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.Test
|
||||
@ -16,7 +15,7 @@ class SpecificationTest {
|
||||
val principal 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> {
|
||||
|
||||
@ -27,7 +26,7 @@ class SpecificationTest {
|
||||
val useSsl by boolean()
|
||||
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)
|
||||
@ -60,9 +59,9 @@ class SpecificationTest {
|
||||
|
||||
private val maxElement by long("elements").list().map { elements -> elements.max() }
|
||||
|
||||
override fun parseValid(configuration: Config): Valid<AtomicLong> {
|
||||
|
||||
return valid(AtomicLong(configuration[maxElement]!!))
|
||||
override fun parseValid(configuration: Config, options: Configuration.Options): Valid<AtomicLong> {
|
||||
val config = configuration.withOptions(options)
|
||||
return valid(AtomicLong(config[maxElement]!!))
|
||||
}
|
||||
}
|
||||
|
||||
@ -111,9 +110,9 @@ class SpecificationTest {
|
||||
|
||||
private val maxElement by long("elements").list().mapValid(::parseMax)
|
||||
|
||||
override fun parseValid(configuration: Config): Valid<AtomicLong> {
|
||||
|
||||
return valid(AtomicLong(configuration[maxElement]))
|
||||
override fun parseValid(configuration: Config, options: Configuration.Options): Valid<AtomicLong> {
|
||||
val config = configuration.withOptions(options)
|
||||
return valid(AtomicLong(config[maxElement]))
|
||||
}
|
||||
}
|
||||
|
||||
@ -159,7 +158,7 @@ class SpecificationTest {
|
||||
@Suppress("unused")
|
||||
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)
|
||||
|
@ -15,5 +15,5 @@ internal fun <VALUE> extractValueWithErrors(errors: Set<Configuration.Validation
|
||||
|
||||
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
|
||||
}
|
@ -11,7 +11,7 @@ java8MinUpdateVersion=171
|
||||
# When incrementing platformVersion make sure to update #
|
||||
# net.corda.core.internal.CordaUtilsKt.PLATFORM_VERSION as well. #
|
||||
# ***************************************************************#
|
||||
platformVersion=6
|
||||
platformVersion=7
|
||||
guavaVersion=28.0-jre
|
||||
# Quasar version to use with Java 8:
|
||||
quasarVersion=0.7.12_r3
|
||||
@ -21,7 +21,7 @@ quasarVersion11=0.8.0_r3
|
||||
jdkClassifier11=jdk11
|
||||
proguardVersion=6.1.1
|
||||
bouncycastleVersion=1.60
|
||||
classgraphVersion=4.8.58
|
||||
classgraphVersion=4.8.68
|
||||
disruptorVersion=3.4.2
|
||||
typesafeConfigVersion=1.3.4
|
||||
jsr305Version=3.0.2
|
||||
@ -30,7 +30,7 @@ snakeYamlVersion=1.19
|
||||
caffeineVersion=2.7.0
|
||||
metricsVersion=4.1.0
|
||||
metricsNewRelicVersion=1.1.1
|
||||
djvmVersion=1.0-RC10
|
||||
djvmVersion=1.1-RC01
|
||||
deterministicRtVersion=1.0-RC02
|
||||
openSourceBranch=https://github.com/corda/corda/blob/release/os/4.4
|
||||
openSourceSamplesBranch=https://github.com/corda/samples/blob/release-V4
|
||||
|
@ -23,7 +23,6 @@ import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import static net.corda.testing.driver.Driver.driver;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class FlowExternalOperationInJavaTest extends AbstractFlowExternalOperationTest {
|
||||
|
||||
@ -32,16 +31,16 @@ public class FlowExternalOperationInJavaTest extends AbstractFlowExternalOperati
|
||||
driver(new DriverParameters().withStartNodesInProcess(true), driver -> {
|
||||
NodeHandle alice = KotlinUtilsKt.getOrThrow(
|
||||
driver.startNode(new NodeParameters().withProvidedName(TestConstants.ALICE_NAME)),
|
||||
Duration.of(20, ChronoUnit.SECONDS)
|
||||
Duration.of(1, ChronoUnit.MINUTES)
|
||||
);
|
||||
NodeHandle bob = KotlinUtilsKt.getOrThrow(
|
||||
driver.startNode(new NodeParameters().withProvidedName(TestConstants.BOB_NAME)),
|
||||
Duration.of(20, ChronoUnit.SECONDS)
|
||||
Duration.of(1, ChronoUnit.MINUTES)
|
||||
);
|
||||
return KotlinUtilsKt.getOrThrow(alice.getRpc().startFlowDynamic(
|
||||
FlowWithExternalOperationInJava.class,
|
||||
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 -> {
|
||||
NodeHandle alice = KotlinUtilsKt.getOrThrow(
|
||||
driver.startNode(new NodeParameters().withProvidedName(TestConstants.ALICE_NAME)),
|
||||
Duration.of(20, ChronoUnit.SECONDS)
|
||||
Duration.of(1, ChronoUnit.MINUTES)
|
||||
);
|
||||
NodeHandle bob = KotlinUtilsKt.getOrThrow(
|
||||
driver.startNode(new NodeParameters().withProvidedName(TestConstants.BOB_NAME)),
|
||||
Duration.of(20, ChronoUnit.SECONDS)
|
||||
Duration.of(1, ChronoUnit.MINUTES)
|
||||
);
|
||||
return KotlinUtilsKt.getOrThrow(alice.getRpc().startFlowDynamic(
|
||||
FlowWithExternalAsyncOperationInJava.class,
|
||||
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 -> {
|
||||
NodeHandle alice = KotlinUtilsKt.getOrThrow(
|
||||
driver.startNode(new NodeParameters().withProvidedName(TestConstants.ALICE_NAME)),
|
||||
Duration.of(20, ChronoUnit.SECONDS)
|
||||
Duration.of(1, ChronoUnit.MINUTES)
|
||||
);
|
||||
NodeHandle bob = KotlinUtilsKt.getOrThrow(
|
||||
driver.startNode(new NodeParameters().withProvidedName(TestConstants.BOB_NAME)),
|
||||
Duration.of(20, ChronoUnit.SECONDS)
|
||||
Duration.of(1, ChronoUnit.MINUTES)
|
||||
);
|
||||
KotlinUtilsKt.getOrThrow(alice.getRpc().startFlowDynamic(
|
||||
FlowWithExternalOperationThatGetsRetriedInJava.class,
|
||||
TestUtils.singleIdentity(bob.getNodeInfo())
|
||||
).getReturnValue(), Duration.of(20, ChronoUnit.SECONDS));
|
||||
).getReturnValue(), Duration.of(1, ChronoUnit.MINUTES));
|
||||
|
||||
HospitalCounts counts = KotlinUtilsKt.getOrThrow(alice.getRpc().startFlowDynamic(
|
||||
GetHospitalCountersFlow.class
|
||||
).getReturnValue(), Duration.of(20, ChronoUnit.SECONDS));
|
||||
assertEquals(1, counts.getDischarge());
|
||||
assertEquals(0, counts.getObservation());
|
||||
assertHospitalCounters(1, 0);
|
||||
|
||||
return null;
|
||||
});
|
||||
|
@ -12,26 +12,55 @@ import net.corda.core.flows.StartableByRPC
|
||||
import net.corda.core.flows.StartableByService
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.concurrent.doOnComplete
|
||||
import net.corda.core.messaging.FlowHandle
|
||||
import net.corda.core.node.AppServiceHub
|
||||
import net.corda.core.node.ServiceHub
|
||||
import net.corda.core.node.services.CordaService
|
||||
import net.corda.core.schemas.MappedSchema
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||
import net.corda.core.utilities.contextLogger
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.core.utilities.unwrap
|
||||
import net.corda.node.services.statemachine.StaffedFlowHospital
|
||||
import org.junit.Before
|
||||
import java.sql.SQLTransientConnectionException
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.Semaphore
|
||||
import java.util.function.Supplier
|
||||
import javax.persistence.Column
|
||||
import javax.persistence.Entity
|
||||
import javax.persistence.Id
|
||||
import javax.persistence.Table
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
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
|
||||
@InitiatingFlow
|
||||
@StartableByService
|
||||
@ -44,11 +73,12 @@ abstract class AbstractFlowExternalOperationTest {
|
||||
@Suspendable
|
||||
override fun call(): Any {
|
||||
log.info("Started my flow")
|
||||
subFlow(PingPongFlow(party))
|
||||
val result = testCode()
|
||||
val session = initiateFlow(party)
|
||||
session.send("hi there")
|
||||
log.info("ServiceHub value = $serviceHub")
|
||||
session.receive<String>()
|
||||
session.sendAndReceive<String>("hi there").unwrap { it }
|
||||
session.sendAndReceive<String>("hi there").unwrap { it }
|
||||
subFlow(PingPongFlow(party))
|
||||
log.info("Finished my flow")
|
||||
return result
|
||||
}
|
||||
@ -64,8 +94,28 @@ abstract class AbstractFlowExternalOperationTest {
|
||||
class FlowWithExternalOperationResponder(val session: FlowSession) : FlowLogic<Unit>() {
|
||||
@Suspendable
|
||||
override fun call() {
|
||||
session.receive<String>()
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
@ -83,7 +133,7 @@ abstract class AbstractFlowExternalOperationTest {
|
||||
fun createFuture(): CompletableFuture<Any> {
|
||||
return CompletableFuture.supplyAsync(Supplier<Any> {
|
||||
log.info("Starting sleep inside of future")
|
||||
Thread.sleep(2000)
|
||||
Thread.sleep(1000)
|
||||
log.info("Finished sleep inside of future")
|
||||
"Here is your return value"
|
||||
}, executorService)
|
||||
@ -182,31 +232,6 @@ abstract class AbstractFlowExternalOperationTest {
|
||||
|
||||
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 DirectlyAccessedServiceHubException : CordaException("Null pointer from accessing flow's serviceHub")
|
||||
|
@ -6,12 +6,7 @@ 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.seconds
|
||||
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.core.utilities.minutes
|
||||
import net.corda.node.services.statemachine.StateTransitionException
|
||||
import net.corda.testing.core.ALICE_NAME
|
||||
import net.corda.testing.core.BOB_NAME
|
||||
@ -21,28 +16,24 @@ import net.corda.testing.driver.driver
|
||||
import org.junit.Test
|
||||
import java.sql.SQLTransientConnectionException
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import java.util.concurrent.TimeoutException
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFailsWith
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class FlowExternalAsyncOperationTest : AbstractFlowExternalOperationTest() {
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `external async operation`() {
|
||||
@Test(timeout = 300_000)
|
||||
fun `external async operation`() {
|
||||
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
|
||||
val alice = startNode(providedName = ALICE_NAME).getOrThrow()
|
||||
val bob = startNode(providedName = BOB_NAME).getOrThrow()
|
||||
alice.rpc.startFlow(::FlowWithExternalAsyncOperation, bob.nodeInfo.singleIdentity())
|
||||
.returnValue.getOrThrow(20.seconds)
|
||||
val (discharged, observation) = alice.rpc.startFlow(::GetHospitalCountersFlow).returnValue.getOrThrow()
|
||||
assertEquals(0, discharged)
|
||||
assertEquals(0, observation)
|
||||
.returnValue.getOrThrow(1.minutes)
|
||||
assertHospitalCounters(0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `external async operation that checks deduplicationId is not rerun when flow is retried`() {
|
||||
@Test(timeout = 300_000)
|
||||
fun `external async operation that checks deduplicationId is not rerun when flow is retried`() {
|
||||
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
|
||||
val alice = startNode(providedName = ALICE_NAME).getOrThrow()
|
||||
val bob = startNode(providedName = BOB_NAME).getOrThrow()
|
||||
@ -50,16 +41,14 @@ class FlowExternalAsyncOperationTest : AbstractFlowExternalOperationTest() {
|
||||
alice.rpc.startFlow(
|
||||
::FlowWithExternalAsyncOperationWithDeduplication,
|
||||
bob.nodeInfo.singleIdentity()
|
||||
).returnValue.getOrThrow(20.seconds)
|
||||
).returnValue.getOrThrow(1.minutes)
|
||||
}
|
||||
val (discharged, observation) = alice.rpc.startFlow(::GetHospitalCountersFlow).returnValue.getOrThrow()
|
||||
assertEquals(1, discharged)
|
||||
assertEquals(0, observation)
|
||||
assertHospitalCounters(1, 0)
|
||||
}
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `external async operation propagates exception to calling flow`() {
|
||||
@Test(timeout = 300_000)
|
||||
fun `external async operation propagates exception to calling flow`() {
|
||||
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
|
||||
val alice = startNode(providedName = ALICE_NAME).getOrThrow()
|
||||
val bob = startNode(providedName = BOB_NAME).getOrThrow()
|
||||
@ -68,100 +57,88 @@ class FlowExternalAsyncOperationTest : AbstractFlowExternalOperationTest() {
|
||||
::FlowWithExternalAsyncOperationPropagatesException,
|
||||
bob.nodeInfo.singleIdentity(),
|
||||
MyCordaException::class.java
|
||||
).returnValue.getOrThrow(20.seconds)
|
||||
).returnValue.getOrThrow(1.minutes)
|
||||
}
|
||||
val (discharged, observation) = alice.rpc.startFlow(::GetHospitalCountersFlow).returnValue.getOrThrow()
|
||||
assertEquals(0, discharged)
|
||||
assertEquals(0, observation)
|
||||
assertHospitalCounters(0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `external async operation exception can be caught in flow`() {
|
||||
@Test(timeout = 300_000)
|
||||
fun `external async operation exception can be caught in flow`() {
|
||||
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
|
||||
val alice = startNode(providedName = ALICE_NAME).getOrThrow()
|
||||
val bob = startNode(providedName = BOB_NAME).getOrThrow()
|
||||
val result = alice.rpc.startFlow(
|
||||
::FlowWithExternalAsyncOperationThatThrowsExceptionAndCaughtInFlow,
|
||||
bob.nodeInfo.singleIdentity()
|
||||
).returnValue.getOrThrow(20.seconds)
|
||||
).returnValue.getOrThrow(1.minutes)
|
||||
assertTrue(result as Boolean)
|
||||
val (discharged, observation) = alice.rpc.startFlow(::GetHospitalCountersFlow).returnValue.getOrThrow()
|
||||
assertEquals(0, discharged)
|
||||
assertEquals(0, observation)
|
||||
assertHospitalCounters(0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `external async operation with exception that hospital keeps for observation does not fail`() {
|
||||
@Test(timeout = 300_000)
|
||||
fun `external async operation with exception that hospital keeps for observation does not fail`() {
|
||||
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
|
||||
val alice = startNode(providedName = ALICE_NAME).getOrThrow()
|
||||
val bob = startNode(providedName = BOB_NAME).getOrThrow()
|
||||
assertFailsWith<TimeoutException> {
|
||||
blockUntilFlowKeptInForObservation {
|
||||
alice.rpc.startFlow(
|
||||
::FlowWithExternalAsyncOperationPropagatesException,
|
||||
bob.nodeInfo.singleIdentity(),
|
||||
HospitalizeFlowException::class.java
|
||||
).returnValue.getOrThrow(20.seconds)
|
||||
)
|
||||
}
|
||||
val (discharged, observation) = alice.rpc.startFlow(::GetHospitalCountersFlow).returnValue.getOrThrow()
|
||||
assertEquals(0, discharged)
|
||||
assertEquals(1, observation)
|
||||
assertHospitalCounters(0, 1)
|
||||
}
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `external async operation with exception that hospital discharges is retried and runs the future again`() {
|
||||
@Test(timeout = 300_000)
|
||||
fun `external async operation with exception that hospital discharges is retried and runs the future again`() {
|
||||
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
|
||||
val alice = startNode(providedName = ALICE_NAME).getOrThrow()
|
||||
val bob = startNode(providedName = BOB_NAME).getOrThrow()
|
||||
assertFailsWith<TimeoutException> {
|
||||
blockUntilFlowKeptInForObservation {
|
||||
alice.rpc.startFlow(
|
||||
::FlowWithExternalAsyncOperationPropagatesException,
|
||||
bob.nodeInfo.singleIdentity(),
|
||||
SQLTransientConnectionException::class.java
|
||||
).returnValue.getOrThrow(20.seconds)
|
||||
)
|
||||
}
|
||||
val (discharged, observation) = alice.rpc.startFlow(::GetHospitalCountersFlow).returnValue.getOrThrow()
|
||||
assertEquals(3, discharged)
|
||||
assertEquals(1, observation)
|
||||
assertHospitalCounters(3, 1)
|
||||
}
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `external async operation that throws exception rather than completing future exceptionally fails with internal exception`() {
|
||||
@Test(timeout = 300_000)
|
||||
fun `external async operation that throws exception rather than completing future exceptionally fails with internal exception`() {
|
||||
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
|
||||
val alice = startNode(providedName = ALICE_NAME).getOrThrow()
|
||||
val bob = startNode(providedName = BOB_NAME).getOrThrow()
|
||||
assertFailsWith<StateTransitionException> {
|
||||
alice.rpc.startFlow(::FlowWithExternalAsyncOperationUnhandledException, bob.nodeInfo.singleIdentity())
|
||||
.returnValue.getOrThrow(20.seconds)
|
||||
.returnValue.getOrThrow(1.minutes)
|
||||
}
|
||||
val (discharged, observation) = alice.rpc.startFlow(::GetHospitalCountersFlow).returnValue.getOrThrow()
|
||||
assertEquals(0, discharged)
|
||||
assertEquals(0, observation)
|
||||
assertHospitalCounters(0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `external async operation that passes serviceHub into process can be retried`() {
|
||||
@Test(timeout = 300_000)
|
||||
fun `external async operation that passes serviceHub into process can be retried`() {
|
||||
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
|
||||
val alice = startNode(providedName = ALICE_NAME).getOrThrow()
|
||||
val bob = startNode(providedName = BOB_NAME).getOrThrow()
|
||||
assertFailsWith<TimeoutException> {
|
||||
blockUntilFlowKeptInForObservation {
|
||||
alice.rpc.startFlow(
|
||||
::FlowWithExternalAsyncOperationThatPassesInServiceHubCanRetry,
|
||||
bob.nodeInfo.singleIdentity()
|
||||
).returnValue.getOrThrow(20.seconds)
|
||||
)
|
||||
}
|
||||
val (discharged, observation) = alice.rpc.startFlow(::GetHospitalCountersFlow).returnValue.getOrThrow()
|
||||
assertEquals(3, discharged)
|
||||
assertEquals(1, observation)
|
||||
assertHospitalCounters(3, 1)
|
||||
}
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `external async operation that accesses serviceHub from flow directly will fail when retried`() {
|
||||
@Test(timeout = 300_000)
|
||||
fun `external async operation that accesses serviceHub from flow directly will fail when retried`() {
|
||||
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
|
||||
val alice = startNode(providedName = ALICE_NAME).getOrThrow()
|
||||
val bob = startNode(providedName = BOB_NAME).getOrThrow()
|
||||
@ -169,23 +146,19 @@ class FlowExternalAsyncOperationTest : AbstractFlowExternalOperationTest() {
|
||||
alice.rpc.startFlow(
|
||||
::FlowWithExternalAsyncOperationThatDirectlyAccessesServiceHubFailsRetry,
|
||||
bob.nodeInfo.singleIdentity()
|
||||
).returnValue.getOrThrow(20.seconds)
|
||||
).returnValue.getOrThrow(1.minutes)
|
||||
}
|
||||
val (discharged, observation) = alice.rpc.startFlow(::GetHospitalCountersFlow).returnValue.getOrThrow()
|
||||
assertEquals(1, discharged)
|
||||
assertEquals(0, observation)
|
||||
assertHospitalCounters(1, 0)
|
||||
}
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `starting multiple futures and joining on their results`() {
|
||||
@Test(timeout = 300_000)
|
||||
fun `starting multiple futures and joining on their results`() {
|
||||
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
|
||||
val alice = startNode(providedName = ALICE_NAME).getOrThrow()
|
||||
val bob = startNode(providedName = BOB_NAME).getOrThrow()
|
||||
alice.rpc.startFlow(::FlowThatStartsMultipleFuturesAndJoins, bob.nodeInfo.singleIdentity()).returnValue.getOrThrow(20.seconds)
|
||||
val (discharged, observation) = alice.rpc.startFlow(::GetHospitalCountersFlow).returnValue.getOrThrow()
|
||||
assertEquals(0, discharged)
|
||||
assertEquals(0, observation)
|
||||
alice.rpc.startFlow(::FlowThatStartsMultipleFuturesAndJoins, bob.nodeInfo.singleIdentity()).returnValue.getOrThrow(1.minutes)
|
||||
assertHospitalCounters(0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
@ -204,12 +177,14 @@ class FlowExternalAsyncOperationTest : AbstractFlowExternalOperationTest() {
|
||||
FlowWithExternalProcess(party) {
|
||||
|
||||
@Suspendable
|
||||
override fun testCode(): Any =
|
||||
await(ExternalAsyncOperation(serviceHub) { _, _ ->
|
||||
override fun testCode(): Any {
|
||||
val e = createException()
|
||||
return await(ExternalAsyncOperation(serviceHub) { _, _ ->
|
||||
CompletableFuture<Any>().apply {
|
||||
completeExceptionally(createException())
|
||||
completeExceptionally(e)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun createException() = when (exceptionType) {
|
||||
HospitalizeFlowException::class.java -> HospitalizeFlowException("keep it around")
|
||||
|
@ -5,40 +5,35 @@ 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.seconds
|
||||
import net.corda.core.utilities.minutes
|
||||
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.junit.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class FlowExternalOperationStartFlowTest : AbstractFlowExternalOperationTest() {
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `starting a flow inside of a flow that starts a future will succeed`() {
|
||||
@Test(timeout = 300_000)
|
||||
fun `starting a flow inside of a flow that starts a future will succeed`() {
|
||||
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
|
||||
val alice = startNode(providedName = ALICE_NAME).getOrThrow()
|
||||
val bob = startNode(providedName = BOB_NAME).getOrThrow()
|
||||
alice.rpc.startFlow(::FlowThatStartsAnotherFlowInAnExternalOperation, bob.nodeInfo.singleIdentity())
|
||||
.returnValue.getOrThrow(40.seconds)
|
||||
val (discharged, observation) = alice.rpc.startFlow(::GetHospitalCountersFlow).returnValue.getOrThrow()
|
||||
assertEquals(0, discharged)
|
||||
assertEquals(0, observation)
|
||||
.returnValue.getOrThrow(1.minutes)
|
||||
assertHospitalCounters(0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `multiple flows can be started and their futures joined from inside a flow`() {
|
||||
@Test(timeout = 300_000)
|
||||
fun `multiple flows can be started and their futures joined from inside a flow`() {
|
||||
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
|
||||
val alice = startNode(providedName = ALICE_NAME).getOrThrow()
|
||||
val bob = startNode(providedName = BOB_NAME).getOrThrow()
|
||||
alice.rpc.startFlow(::ForkJoinFlows, bob.nodeInfo.singleIdentity())
|
||||
.returnValue.getOrThrow(40.seconds)
|
||||
val (discharged, observation) = alice.rpc.startFlow(::GetHospitalCountersFlow).returnValue.getOrThrow()
|
||||
assertEquals(0, discharged)
|
||||
assertEquals(0, observation)
|
||||
.returnValue.getOrThrow(1.minutes)
|
||||
assertHospitalCounters(0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,13 +10,7 @@ import net.corda.core.messaging.startFlow
|
||||
import net.corda.core.node.services.queryBy
|
||||
import net.corda.core.transactions.TransactionBuilder
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.core.utilities.seconds
|
||||
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.core.utilities.minutes
|
||||
import net.corda.testing.contracts.DummyContract
|
||||
import net.corda.testing.contracts.DummyState
|
||||
import net.corda.testing.core.ALICE_NAME
|
||||
@ -26,30 +20,25 @@ import net.corda.testing.driver.DriverParameters
|
||||
import net.corda.testing.driver.driver
|
||||
import net.corda.testing.node.internal.cordappsForPackages
|
||||
import org.junit.Test
|
||||
import java.lang.IllegalStateException
|
||||
import java.sql.SQLTransientConnectionException
|
||||
import java.util.concurrent.TimeoutException
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFailsWith
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class FlowExternalOperationTest : AbstractFlowExternalOperationTest() {
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `external operation`() {
|
||||
@Test(timeout = 300_000)
|
||||
fun `external operation`() {
|
||||
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
|
||||
val alice = startNode(providedName = ALICE_NAME).getOrThrow()
|
||||
val bob = startNode(providedName = BOB_NAME).getOrThrow()
|
||||
alice.rpc.startFlow(::FlowWithExternalOperation, bob.nodeInfo.singleIdentity())
|
||||
.returnValue.getOrThrow(20.seconds)
|
||||
val (discharged, observation) = alice.rpc.startFlow(::GetHospitalCountersFlow).returnValue.getOrThrow()
|
||||
assertEquals(0, discharged)
|
||||
assertEquals(0, observation)
|
||||
.returnValue.getOrThrow(1.minutes)
|
||||
assertHospitalCounters(0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `external operation that checks deduplicationId is not rerun when flow is retried`() {
|
||||
@Test(timeout = 300_000)
|
||||
fun `external operation that checks deduplicationId is not rerun when flow is retried`() {
|
||||
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
|
||||
val alice = startNode(providedName = ALICE_NAME).getOrThrow()
|
||||
val bob = startNode(providedName = BOB_NAME).getOrThrow()
|
||||
@ -57,16 +46,14 @@ class FlowExternalOperationTest : AbstractFlowExternalOperationTest() {
|
||||
alice.rpc.startFlow(
|
||||
::FlowWithExternalOperationWithDeduplication,
|
||||
bob.nodeInfo.singleIdentity()
|
||||
).returnValue.getOrThrow(20.seconds)
|
||||
).returnValue.getOrThrow(1.minutes)
|
||||
}
|
||||
val (discharged, observation) = alice.rpc.startFlow(::GetHospitalCountersFlow).returnValue.getOrThrow()
|
||||
assertEquals(1, discharged)
|
||||
assertEquals(0, observation)
|
||||
assertHospitalCounters(1, 0)
|
||||
}
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `external operation propagates exception to calling flow`() {
|
||||
@Test(timeout = 300_000)
|
||||
fun `external operation propagates exception to calling flow`() {
|
||||
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
|
||||
val alice = startNode(providedName = ALICE_NAME).getOrThrow()
|
||||
val bob = startNode(providedName = BOB_NAME).getOrThrow()
|
||||
@ -75,82 +62,72 @@ class FlowExternalOperationTest : AbstractFlowExternalOperationTest() {
|
||||
::FlowWithExternalOperationPropagatesException,
|
||||
bob.nodeInfo.singleIdentity(),
|
||||
MyCordaException::class.java
|
||||
).returnValue.getOrThrow(20.seconds)
|
||||
).returnValue.getOrThrow(1.minutes)
|
||||
}
|
||||
val (discharged, observation) = alice.rpc.startFlow(::GetHospitalCountersFlow).returnValue.getOrThrow()
|
||||
assertEquals(0, discharged)
|
||||
assertEquals(0, observation)
|
||||
assertHospitalCounters(0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `external operation exception can be caught in flow`() {
|
||||
@Test(timeout = 300_000)
|
||||
fun `external operation exception can be caught in flow`() {
|
||||
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
|
||||
val alice = startNode(providedName = ALICE_NAME).getOrThrow()
|
||||
val bob = startNode(providedName = BOB_NAME).getOrThrow()
|
||||
alice.rpc.startFlow(::FlowWithExternalOperationThatThrowsExceptionAndCaughtInFlow, bob.nodeInfo.singleIdentity())
|
||||
.returnValue.getOrThrow(20.seconds)
|
||||
val (discharged, observation) = alice.rpc.startFlow(::GetHospitalCountersFlow).returnValue.getOrThrow()
|
||||
assertEquals(0, discharged)
|
||||
assertEquals(0, observation)
|
||||
.returnValue.getOrThrow(1.minutes)
|
||||
assertHospitalCounters(0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `external operation with exception that hospital keeps for observation does not fail`() {
|
||||
@Test(timeout = 300_000)
|
||||
fun `external operation with exception that hospital keeps for observation does not fail`() {
|
||||
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
|
||||
val alice = startNode(providedName = ALICE_NAME).getOrThrow()
|
||||
val bob = startNode(providedName = BOB_NAME).getOrThrow()
|
||||
assertFailsWith<TimeoutException> {
|
||||
blockUntilFlowKeptInForObservation {
|
||||
alice.rpc.startFlow(
|
||||
::FlowWithExternalOperationPropagatesException,
|
||||
bob.nodeInfo.singleIdentity(),
|
||||
HospitalizeFlowException::class.java
|
||||
).returnValue.getOrThrow(20.seconds)
|
||||
)
|
||||
}
|
||||
val (discharged, observation) = alice.rpc.startFlow(::GetHospitalCountersFlow).returnValue.getOrThrow()
|
||||
assertEquals(0, discharged)
|
||||
assertEquals(1, observation)
|
||||
assertHospitalCounters(0, 1)
|
||||
}
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `external operation with exception that hospital discharges is retried and runs the external operation again`() {
|
||||
@Test(timeout = 300_000)
|
||||
fun `external operation with exception that hospital discharges is retried and runs the external operation again`() {
|
||||
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
|
||||
val alice = startNode(providedName = ALICE_NAME).getOrThrow()
|
||||
val bob = startNode(providedName = BOB_NAME).getOrThrow()
|
||||
assertFailsWith<TimeoutException> {
|
||||
blockUntilFlowKeptInForObservation {
|
||||
alice.rpc.startFlow(
|
||||
::FlowWithExternalOperationPropagatesException,
|
||||
bob.nodeInfo.singleIdentity(),
|
||||
SQLTransientConnectionException::class.java
|
||||
).returnValue.getOrThrow(20.seconds)
|
||||
)
|
||||
}
|
||||
val (discharged, observation) = alice.rpc.startFlow(::GetHospitalCountersFlow).returnValue.getOrThrow()
|
||||
assertEquals(3, discharged)
|
||||
assertEquals(1, observation)
|
||||
assertHospitalCounters(3, 1)
|
||||
}
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `external async operation that passes serviceHub into process can be retried`() {
|
||||
@Test(timeout = 300_000)
|
||||
fun `external async operation that passes serviceHub into process can be retried`() {
|
||||
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
|
||||
val alice = startNode(providedName = ALICE_NAME).getOrThrow()
|
||||
val bob = startNode(providedName = BOB_NAME).getOrThrow()
|
||||
assertFailsWith<TimeoutException> {
|
||||
blockUntilFlowKeptInForObservation {
|
||||
alice.rpc.startFlow(
|
||||
::FlowWithExternalOperationThatPassesInServiceHubCanRetry,
|
||||
bob.nodeInfo.singleIdentity()
|
||||
).returnValue.getOrThrow(20.seconds)
|
||||
)
|
||||
}
|
||||
val (discharged, observation) = alice.rpc.startFlow(::GetHospitalCountersFlow).returnValue.getOrThrow()
|
||||
assertEquals(3, discharged)
|
||||
assertEquals(1, observation)
|
||||
assertHospitalCounters(3, 1)
|
||||
}
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `external async operation that accesses serviceHub from flow directly will fail when retried`() {
|
||||
@Test(timeout = 300_000)
|
||||
fun `external async operation that accesses serviceHub from flow directly will fail when retried`() {
|
||||
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
|
||||
val alice = startNode(providedName = ALICE_NAME).getOrThrow()
|
||||
val bob = startNode(providedName = BOB_NAME).getOrThrow()
|
||||
@ -158,16 +135,14 @@ class FlowExternalOperationTest : AbstractFlowExternalOperationTest() {
|
||||
alice.rpc.startFlow(
|
||||
::FlowWithExternalOperationThatDirectlyAccessesServiceHubFailsRetry,
|
||||
bob.nodeInfo.singleIdentity()
|
||||
).returnValue.getOrThrow(20.seconds)
|
||||
).returnValue.getOrThrow(1.minutes)
|
||||
}
|
||||
val (discharged, observation) = alice.rpc.startFlow(::GetHospitalCountersFlow).returnValue.getOrThrow()
|
||||
assertEquals(1, discharged)
|
||||
assertEquals(0, observation)
|
||||
assertHospitalCounters(1, 0)
|
||||
}
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `vault can be queried`() {
|
||||
@Test(timeout = 300_000)
|
||||
fun `vault can be queried`() {
|
||||
driver(
|
||||
DriverParameters(
|
||||
cordappsForAllNodes = cordappsForPackages(DummyState::class.packageName),
|
||||
@ -176,64 +151,62 @@ class FlowExternalOperationTest : AbstractFlowExternalOperationTest() {
|
||||
) {
|
||||
val alice = startNode(providedName = ALICE_NAME).getOrThrow()
|
||||
val success = alice.rpc.startFlow(::FlowWithWithExternalOperationThatQueriesVault)
|
||||
.returnValue.getOrThrow(20.seconds)
|
||||
.returnValue.getOrThrow(1.minutes)
|
||||
assertTrue(success)
|
||||
}
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `data can be persisted to node database via entity manager`() {
|
||||
@Test(timeout = 300_000)
|
||||
fun `data can be persisted to node database via entity manager`() {
|
||||
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
|
||||
val alice = startNode(providedName = ALICE_NAME).getOrThrow()
|
||||
val success = alice.rpc.startFlow(::FlowWithExternalOperationThatPersistsViaEntityManager)
|
||||
.returnValue.getOrThrow(20.seconds)
|
||||
.returnValue.getOrThrow(1.minutes)
|
||||
assertTrue(success)
|
||||
}
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `data can be persisted to node database via jdbc session`() {
|
||||
@Test(timeout = 300_000)
|
||||
fun `data can be persisted to node database via jdbc session`() {
|
||||
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
|
||||
val alice = startNode(providedName = ALICE_NAME).getOrThrow()
|
||||
val success = alice.rpc.startFlow(::FlowWithExternalOperationThatPersistsViaJdbcSession)
|
||||
.returnValue.getOrThrow(20.seconds)
|
||||
.returnValue.getOrThrow(1.minutes)
|
||||
assertTrue(success)
|
||||
}
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `data can be persisted to node database via servicehub database transaction`() {
|
||||
@Test(timeout = 300_000)
|
||||
fun `data can be persisted to node database via servicehub database transaction`() {
|
||||
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
|
||||
val alice = startNode(providedName = ALICE_NAME).getOrThrow()
|
||||
val success = alice.rpc.startFlow(::FlowWithExternalOperationThatPersistsViaDatabaseTransaction)
|
||||
.returnValue.getOrThrow(20.seconds)
|
||||
.returnValue.getOrThrow(1.minutes)
|
||||
assertTrue(success)
|
||||
}
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `data can be persisted to node database in external operation and read from another process once finished`() {
|
||||
@Test(timeout = 300_000)
|
||||
fun `data can be persisted to node database in external operation and read from another process once finished`() {
|
||||
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
|
||||
val alice = startNode(providedName = ALICE_NAME).getOrThrow()
|
||||
val success = alice.rpc.startFlow(::FlowWithExternalOperationThatPersistsToDatabaseAndReadsFromExternalOperation)
|
||||
.returnValue.getOrThrow(20.seconds)
|
||||
.returnValue.getOrThrow(1.minutes)
|
||||
assertTrue(success)
|
||||
}
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `external operation can be retried when an error occurs inside of database transaction`() {
|
||||
@Test(timeout = 300_000)
|
||||
fun `external operation can be retried when an error occurs inside of database transaction`() {
|
||||
driver(DriverParameters(notarySpecs = emptyList(), startNodesInProcess = true)) {
|
||||
val alice = startNode(providedName = ALICE_NAME).getOrThrow()
|
||||
val bob = startNode(providedName = BOB_NAME).getOrThrow()
|
||||
val success = alice.rpc.startFlow(
|
||||
::FlowWithExternalOperationThatErrorsInsideOfDatabaseTransaction,
|
||||
bob.nodeInfo.singleIdentity()
|
||||
).returnValue.getOrThrow(20.seconds)
|
||||
).returnValue.getOrThrow(1.minutes)
|
||||
assertTrue(success as Boolean)
|
||||
val (discharged, observation) = alice.rpc.startFlow(::GetHospitalCountersFlow).returnValue.getOrThrow()
|
||||
assertEquals(1, discharged)
|
||||
assertEquals(0, observation)
|
||||
assertHospitalCounters(1, 0)
|
||||
}
|
||||
}
|
||||
|
||||
@ -260,7 +233,10 @@ class FlowExternalOperationTest : AbstractFlowExternalOperationTest() {
|
||||
FlowWithExternalProcess(party) {
|
||||
|
||||
@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) {
|
||||
HospitalizeFlowException::class.java -> HospitalizeFlowException("keep it around")
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
@ -21,14 +21,18 @@ import net.corda.testing.internal.services.InternalMockAttachmentStorage
|
||||
import net.corda.testing.node.internal.FINANCE_CONTRACTS_CORDAPP
|
||||
import net.corda.testing.services.MockAttachmentStorage
|
||||
import org.apache.commons.io.IOUtils
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.Assert.assertArrayEquals
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNull
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.InputStream
|
||||
import java.net.URL
|
||||
import kotlin.test.assertFailsWith
|
||||
import kotlin.test.fail
|
||||
|
||||
class AttachmentsClassLoaderTests {
|
||||
companion object {
|
||||
@ -84,14 +88,29 @@ 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)
|
||||
fun `Dynamically load AnotherDummyContract from isolated contracts jar using the AttachmentsClassLoader`() {
|
||||
val isolatedId = importAttachment(ISOLATED_CONTRACTS_JAR_PATH.openStream(), "app", "isolated.jar")
|
||||
|
||||
val classloader = createClassloader(isolatedId)
|
||||
val contractClass = Class.forName(ISOLATED_CONTRACT_CLASS_NAME, true, classloader)
|
||||
val contract = contractClass.getDeclaredConstructor().newInstance() as Contract
|
||||
assertEquals("helloworld", contract.declaredField<Any?>("magicString").value)
|
||||
createClassloader(isolatedId).use { classloader ->
|
||||
val contractClass = Class.forName(ISOLATED_CONTRACT_CLASS_NAME, true, classloader)
|
||||
val contract = contractClass.getDeclaredConstructor().newInstance() as Contract
|
||||
assertEquals("helloworld", contract.declaredField<Any?>("magicString").value)
|
||||
}
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
@ -100,7 +119,7 @@ class AttachmentsClassLoaderTests {
|
||||
val att2 = importAttachment(ISOLATED_CONTRACTS_JAR_PATH_V4.openStream(), "app", "isolated-4.0.jar")
|
||||
|
||||
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")
|
||||
|
||||
// does not throw OverlappingAttachments exception
|
||||
createClassloader(listOf(isolatedId, isolatedSignedId))
|
||||
createClassloader(listOf(isolatedId, isolatedSignedId)).use {}
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
@ -120,7 +139,7 @@ class AttachmentsClassLoaderTests {
|
||||
val att2 = importAttachment(FINANCE_CONTRACTS_CORDAPP.jarFile.inputStream(), "app", "finance.jar")
|
||||
|
||||
// does not throw OverlappingAttachments exception
|
||||
createClassloader(listOf(att1, att2))
|
||||
createClassloader(listOf(att1, att2)).use {}
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
@ -128,12 +147,13 @@ class AttachmentsClassLoaderTests {
|
||||
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 cl = createClassloader(listOf(att1, att2))
|
||||
val txt = IOUtils.toString(cl.getResourceAsStream("file1.txt"), Charsets.UTF_8.name())
|
||||
assertEquals("some data", txt)
|
||||
createClassloader(listOf(att1, att2)).use { cl ->
|
||||
val txt = IOUtils.toString(cl.getResourceAsStream("file1.txt"), Charsets.UTF_8.name())
|
||||
assertEquals("some data", txt)
|
||||
|
||||
val txt1 = IOUtils.toString(cl.getResourceAsStream("file2.txt"), Charsets.UTF_8.name())
|
||||
assertEquals("some other data", txt1)
|
||||
val txt1 = IOUtils.toString(cl.getResourceAsStream("file2.txt"), Charsets.UTF_8.name())
|
||||
assertEquals("some other data", txt1)
|
||||
}
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
@ -141,9 +161,10 @@ class AttachmentsClassLoaderTests {
|
||||
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 cl = createClassloader(listOf(att1, att2))
|
||||
val txt = IOUtils.toString(cl.getResourceAsStream("file1.txt"), Charsets.UTF_8.name())
|
||||
assertEquals("same data", txt)
|
||||
createClassloader(listOf(att1, att2)).use { cl ->
|
||||
val txt = IOUtils.toString(cl.getResourceAsStream("file1.txt"), Charsets.UTF_8.name())
|
||||
assertEquals("same data", txt)
|
||||
}
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
@ -152,7 +173,7 @@ class AttachmentsClassLoaderTests {
|
||||
val att1 = importAttachment(fakeAttachment(path, "some data").inputStream(), "app", "file1.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 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)
|
||||
@ -170,7 +191,7 @@ class AttachmentsClassLoaderTests {
|
||||
val att2 = importAttachment(fakeAttachment("meta-inf/services/com.example.something", "some other data").inputStream(), "app", "file2.jar")
|
||||
|
||||
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")
|
||||
|
||||
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 att2 = importAttachment(fakeAttachment("net/corda/finance/contracts/isolated/AnotherDummyContract\$State.class", "some attackdata").inputStream(), "app", "file2.jar")
|
||||
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 trustedClassJar = importAttachment(fakeAttachment("/com/example/something/VirtuousClass.class", "some other data").inputStream(), "app", "file3.jar")
|
||||
|
||||
createClassloader(listOf(trustedResourceJar, untrustedResourceJar, trustedClassJar))
|
||||
|
||||
assertFailsWith(TransactionVerificationException.UntrustedAttachmentsException::class) {
|
||||
createClassloader(listOf(trustedResourceJar, untrustedResourceJar, trustedClassJar, untrustedClassJar))
|
||||
createClassloader(listOf(trustedResourceJar, untrustedResourceJar, trustedClassJar)).use {
|
||||
assertFailsWith(TransactionVerificationException.UntrustedAttachmentsException::class) {
|
||||
createClassloader(listOf(trustedResourceJar, untrustedResourceJar, trustedClassJar, untrustedClassJar)).use {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -257,7 +278,7 @@ class AttachmentsClassLoaderTests {
|
||||
signers = listOf(keyPairA.public, keyPairB.public)
|
||||
)
|
||||
|
||||
createClassloader(untrustedAttachment)
|
||||
createClassloader(untrustedAttachment).use {}
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
@ -287,7 +308,7 @@ class AttachmentsClassLoaderTests {
|
||||
signers = listOf(keyPairA.public, keyPairB.public)
|
||||
)
|
||||
|
||||
createClassloader(untrustedAttachment)
|
||||
createClassloader(untrustedAttachment).use {}
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
@ -306,7 +327,7 @@ class AttachmentsClassLoaderTests {
|
||||
)
|
||||
|
||||
assertFailsWith(TransactionVerificationException.UntrustedAttachmentsException::class) {
|
||||
createClassloader(untrustedAttachment)
|
||||
createClassloader(untrustedAttachment).use {}
|
||||
}
|
||||
}
|
||||
|
||||
@ -337,7 +358,7 @@ class AttachmentsClassLoaderTests {
|
||||
)
|
||||
|
||||
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
|
||||
createClassloader(inheritedTrustAttachment)
|
||||
|
||||
assertFailsWith(TransactionVerificationException.UntrustedAttachmentsException::class) {
|
||||
createClassloader(untrustedAttachment)
|
||||
createClassloader(inheritedTrustAttachment).use {
|
||||
assertFailsWith(TransactionVerificationException.UntrustedAttachmentsException::class) {
|
||||
createClassloader(untrustedAttachment).use {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -421,7 +442,7 @@ class AttachmentsClassLoaderTests {
|
||||
)
|
||||
|
||||
assertFailsWith(TransactionVerificationException.UntrustedAttachmentsException::class) {
|
||||
createClassloader(untrustedAttachment)
|
||||
createClassloader(untrustedAttachment).use {}
|
||||
}
|
||||
}
|
||||
|
||||
@ -446,6 +467,6 @@ class AttachmentsClassLoaderTests {
|
||||
signers = listOf(keyPairA.public)
|
||||
)
|
||||
|
||||
createClassloader(trustedAttachment)
|
||||
createClassloader(trustedAttachment).use {}
|
||||
}
|
||||
}
|
||||
|
@ -101,7 +101,8 @@ task copyQuasarJar(type: Copy) {
|
||||
|
||||
jar {
|
||||
finalizedBy(copyQuasarJar)
|
||||
baseName 'corda-core'
|
||||
archiveBaseName = 'corda-core'
|
||||
archiveClassifier = ''
|
||||
}
|
||||
|
||||
configurations {
|
||||
|
@ -49,7 +49,7 @@ interface Attachment : NamedByHash {
|
||||
|
||||
/**
|
||||
* 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
|
||||
fun extractFile(path: String, outputTo: OutputStream) = openAsJAR().use { it.extractFile(path, outputTo) }
|
||||
|
@ -108,7 +108,7 @@ object Crypto {
|
||||
AlgorithmIdentifier(X9ObjectIdentifiers.ecdsa_with_SHA256, SECObjectIdentifiers.secp256k1),
|
||||
listOf(AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, SECObjectIdentifiers.secp256k1)),
|
||||
cordaBouncyCastleProvider.name,
|
||||
"ECDSA",
|
||||
"EC",
|
||||
"SHA256withECDSA",
|
||||
ECNamedCurveTable.getParameterSpec("secp256k1"),
|
||||
256,
|
||||
@ -123,7 +123,7 @@ object Crypto {
|
||||
AlgorithmIdentifier(X9ObjectIdentifiers.ecdsa_with_SHA256, SECObjectIdentifiers.secp256r1),
|
||||
listOf(AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, SECObjectIdentifiers.secp256r1)),
|
||||
cordaBouncyCastleProvider.name,
|
||||
"ECDSA",
|
||||
"EC",
|
||||
"SHA256withECDSA",
|
||||
ECNamedCurveTable.getParameterSpec("secp256r1"),
|
||||
256,
|
||||
|
@ -2,7 +2,6 @@ package net.corda.core.crypto
|
||||
|
||||
import net.corda.core.KeepForDJVM
|
||||
import net.corda.core.identity.AnonymousParty
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
import java.security.PublicKey
|
||||
|
||||
@KeepForDJVM
|
||||
|
@ -1,6 +1,7 @@
|
||||
package net.corda.core.flows
|
||||
|
||||
import co.paralleluniverse.fibers.Suspendable
|
||||
import net.corda.core.CordaInternal
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.isFulfilledBy
|
||||
import net.corda.core.identity.Party
|
||||
@ -45,6 +46,13 @@ class FinalityFlow private constructor(val transaction: SignedTransaction,
|
||||
private val sessions: Collection<FlowSession>,
|
||||
private val newApi: Boolean,
|
||||
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)
|
||||
constructor(transaction: SignedTransaction, extraRecipients: Set<Party>, progressTracker: ProgressTracker) : this(
|
||||
transaction, extraRecipients, progressTracker, emptyList(), false
|
||||
|
@ -1,7 +1,5 @@
|
||||
package net.corda.core.flows
|
||||
|
||||
import net.corda.core.internal.ServiceHubCoreInternal
|
||||
import net.corda.core.node.ServiceHub
|
||||
import java.util.concurrent.CompletableFuture
|
||||
|
||||
/**
|
||||
|
@ -24,9 +24,6 @@ import net.corda.core.messaging.DataFeed
|
||||
import net.corda.core.node.NodeInfo
|
||||
import net.corda.core.node.ServiceHub
|
||||
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.utilities.ProgressTracker
|
||||
import net.corda.core.utilities.UntrustworthyData
|
||||
@ -130,6 +127,32 @@ abstract class FlowLogic<out T> {
|
||||
*/
|
||||
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
|
||||
* 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
|
||||
internal fun <R : Any> FlowSession.sendAndReceiveWithRetry(receiveType: Class<R>, payload: Any): UntrustworthyData<R> {
|
||||
val request = FlowIORequest.SendAndReceive(
|
||||
sessionToMessage = mapOf(this to payload.serialize(context = SerializationDefaults.P2P_CONTEXT)),
|
||||
sessionToMessage = stateMachine.serialize(mapOf(this to payload)),
|
||||
shouldRetrySend = true
|
||||
)
|
||||
return stateMachine.suspend(request, maySkipCheckpoint = false)[this]!!.checkPayloadIs(receiveType)
|
||||
@ -333,7 +356,7 @@ abstract class FlowLogic<out T> {
|
||||
@JvmOverloads
|
||||
fun sendAll(payload: Any, sessions: Set<FlowSession>, maySkipCheckpoint: Boolean = false) {
|
||||
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
|
||||
@JvmOverloads
|
||||
fun sendAll(payloadsPerSession: Map<FlowSession, Any>, maySkipCheckpoint: Boolean = false) {
|
||||
fun sendAllMap(payloadsPerSession: Map<FlowSession, Any>, maySkipCheckpoint: Boolean = false) {
|
||||
val request = FlowIORequest.Send(
|
||||
sessionToMessage = serializePayloads(payloadsPerSession)
|
||||
sessionToMessage = stateMachine.serialize(payloadsPerSession)
|
||||
)
|
||||
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
|
||||
* 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
|
||||
@Throws(FlowException::class)
|
||||
open fun <R> subFlow(subLogic: FlowLogic<R>): R {
|
||||
subLogic.stateMachine = stateMachine
|
||||
maybeWireUpProgressTracking(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)}" }
|
||||
return result
|
||||
}
|
||||
@ -540,18 +551,6 @@ abstract class FlowLogic<out T> {
|
||||
_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>) {
|
||||
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
|
||||
fun <R : Any> await(operation: FlowExternalAsyncOperation<R>): R {
|
||||
// Wraps the passed in [FlowExternalAsyncOperation] so its [CompletableFuture] can be converted into a [CordaFuture]
|
||||
val flowAsyncOperation = object : FlowAsyncOperation<R>, WrappedFlowExternalAsyncOperation<R> {
|
||||
override val operation = operation
|
||||
override fun execute(deduplicationId: String): CordaFuture<R> {
|
||||
return this.operation.execute(deduplicationId).asCordaFuture()
|
||||
}
|
||||
}
|
||||
val flowAsyncOperation = WrappedFlowExternalAsyncOperation(operation)
|
||||
val request = FlowIORequest.ExecuteAsyncOperation(flowAsyncOperation)
|
||||
return stateMachine.suspend(request, false)
|
||||
}
|
||||
@ -598,42 +592,82 @@ abstract class FlowLogic<out T> {
|
||||
*/
|
||||
@Suspendable
|
||||
fun <R : Any> await(operation: FlowExternalOperation<R>): R {
|
||||
val flowAsyncOperation = object : FlowAsyncOperation<R>, WrappedFlowExternalOperation<R> {
|
||||
override val serviceHub = this@FlowLogic.serviceHub as ServiceHubCoreInternal
|
||||
override val operation = operation
|
||||
override fun execute(deduplicationId: String): CordaFuture<R> {
|
||||
// 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]
|
||||
return CompletableFuture.supplyAsync(
|
||||
Supplier { this.operation.execute(deduplicationId) },
|
||||
serviceHub.externalOperationExecutor
|
||||
).asCordaFuture()
|
||||
}
|
||||
}
|
||||
val flowAsyncOperation = WrappedFlowExternalOperation(serviceHub as ServiceHubCoreInternal, operation)
|
||||
val request = FlowIORequest.ExecuteAsyncOperation(flowAsyncOperation)
|
||||
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 interface WrappedFlowExternalAsyncOperation<R : Any> {
|
||||
val operation: FlowExternalAsyncOperation<R>
|
||||
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 is also needed by Kryo to properly keep a reference to [ServiceHub] so that
|
||||
* 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 interface WrappedFlowExternalOperation<R : Any> {
|
||||
val serviceHub: ServiceHub
|
||||
private class WrappedFlowExternalOperation<R : Any>(
|
||||
val serviceHub: ServiceHubCoreInternal,
|
||||
val operation: FlowExternalOperation<R>
|
||||
) : FlowAsyncOperation<R> {
|
||||
override fun execute(deduplicationId: String): CordaFuture<R> {
|
||||
// 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]
|
||||
return CompletableFuture.supplyAsync(
|
||||
Supplier { this.operation.execute(deduplicationId) },
|
||||
serviceHub.externalOperationExecutor
|
||||
).asCordaFuture()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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")
|
||||
}
|
@ -28,7 +28,7 @@ import java.util.jar.JarInputStream
|
||||
|
||||
// *Internal* Corda-specific utilities.
|
||||
|
||||
const val PLATFORM_VERSION = 6
|
||||
const val PLATFORM_VERSION = 7
|
||||
|
||||
fun ServicesForResolution.ensureMinimumPlatformVersion(requiredMinPlatformVersion: Int, feature: String) {
|
||||
checkMinimumPlatformVersion(networkParameters.minimumPlatformVersion, requiredMinPlatformVersion, feature)
|
||||
@ -48,6 +48,20 @@ fun checkMinimumPlatformVersion(minimumPlatformVersion: Int, requiredMinPlatform
|
||||
@Throws(NumberFormatException::class)
|
||||
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. */
|
||||
@DeleteForDJVM
|
||||
fun TransactionBuilder.toWireTransaction(services: ServicesForResolution, serializationContext: SerializationContext): WireTransaction {
|
||||
|
@ -8,6 +8,7 @@ import net.corda.core.context.InvocationContext
|
||||
import net.corda.core.flows.*
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.node.ServiceHub
|
||||
import net.corda.core.serialization.SerializedBytes
|
||||
import org.slf4j.Logger
|
||||
|
||||
/** 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
|
||||
fun <SUSPENDRETURN : Any> suspend(ioRequest: FlowIORequest<SUSPENDRETURN>, maySkipCheckpoint: Boolean): SUSPENDRETURN
|
||||
|
||||
fun serialize(payloads: Map<FlowSession, Any>): Map<FlowSession, SerializedBytes<Any>>
|
||||
|
||||
@Suspendable
|
||||
fun initiateFlow(destination: Destination, wellKnownParty: Party): FlowSession
|
||||
|
||||
@ -25,7 +28,7 @@ interface FlowStateMachine<FLOWRETURN> {
|
||||
fun recordAuditEvent(eventType: String, comment: String, extraAuditData: Map<String, String>)
|
||||
|
||||
@Suspendable
|
||||
fun <SUBFLOWRETURN> subFlow(subFlow: FlowLogic<SUBFLOWRETURN>): SUBFLOWRETURN
|
||||
fun <SUBFLOWRETURN> subFlow(currentFlow: FlowLogic<*>, subFlow: FlowLogic<SUBFLOWRETURN>): SUBFLOWRETURN
|
||||
|
||||
@Suspendable
|
||||
fun flowStackSnapshot(flowClass: Class<out FlowLogic<*>>): FlowStackSnapshot?
|
||||
@ -44,4 +47,5 @@ interface FlowStateMachine<FLOWRETURN> {
|
||||
val ourIdentity: Party
|
||||
val ourSenderUUID: String?
|
||||
val creationTime: Long
|
||||
val isKilled: Boolean
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ package net.corda.core.internal
|
||||
|
||||
import net.corda.core.DeleteForDJVM
|
||||
import net.corda.core.KeepForDJVM
|
||||
import net.corda.core.StubOutForDJVM
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.crypto.DigitalSignature
|
||||
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 }
|
||||
|
||||
/** Returns the location of this class. */
|
||||
@get:StubOutForDJVM
|
||||
val Class<*>.location: URL get() = protectionDomain.codeSource.location
|
||||
|
||||
/** Convenience method to get the package name of a class literal. */
|
||||
|
@ -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
|
||||
}
|
@ -2,6 +2,7 @@ package net.corda.core.internal
|
||||
|
||||
import co.paralleluniverse.fibers.Suspendable
|
||||
import net.corda.core.DeleteForDJVM
|
||||
import net.corda.core.internal.notary.NotaryService
|
||||
import net.corda.core.node.ServiceHub
|
||||
import net.corda.core.node.StatesToRecord
|
||||
import java.util.concurrent.ExecutorService
|
||||
@ -14,6 +15,11 @@ interface ServiceHubCoreInternal : ServiceHub {
|
||||
|
||||
val attachmentTrustCalculator: AttachmentTrustCalculator
|
||||
|
||||
/**
|
||||
* Optional `NotaryService` which will be `null` for all non-Notary nodes.
|
||||
*/
|
||||
val notaryService: NotaryService?
|
||||
|
||||
fun createTransactionsResolver(flow: ResolveTransactionsFlow): TransactionsResolver
|
||||
}
|
||||
|
||||
|
@ -77,7 +77,12 @@ fun <T : Any> deserialiseComponentGroup(componentGroups: List<ComponentGroup>,
|
||||
try {
|
||||
factory.deserialize(component, clazz.java, context)
|
||||
} 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) {
|
||||
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.
|
||||
*/
|
||||
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:
|
||||
|
@ -4,7 +4,6 @@ package net.corda.core.internal
|
||||
|
||||
import net.corda.core.KeepForDJVM
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import org.bouncycastle.asn1.ASN1Encodable
|
||||
import org.bouncycastle.asn1.ASN1ObjectIdentifier
|
||||
import org.bouncycastle.asn1.x500.AttributeTypeAndValue
|
||||
import org.bouncycastle.asn1.x500.X500Name
|
||||
|
@ -14,6 +14,19 @@ abstract class NotaryService : SingletonSerializeAsToken() {
|
||||
abstract val services: ServiceHub
|
||||
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 stop()
|
||||
|
||||
@ -22,4 +35,18 @@ abstract class NotaryService : SingletonSerializeAsToken() {
|
||||
* @param otherPartySession client [Party] making the request
|
||||
*/
|
||||
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")
|
||||
}
|
||||
}
|
@ -252,7 +252,7 @@ interface CordaRPCOps : RPCOps {
|
||||
* Note: This operation may be restricted only to node administrators.
|
||||
* @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 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.
|
||||
fun acceptNewNetworkParameters(parametersHash: SecureHash)
|
||||
|
@ -380,6 +380,26 @@ interface ServiceHub : ServicesForResolution {
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
* @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.
|
||||
*
|
||||
* @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
|
||||
|
||||
@ -404,6 +442,24 @@ interface ServiceHub : ServicesForResolution {
|
||||
* 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].
|
||||
*
|
||||
* 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>)
|
||||
|
||||
|
@ -8,7 +8,6 @@ import net.corda.core.contracts.ContractState
|
||||
import net.corda.core.contracts.StateRef
|
||||
import net.corda.core.contracts.UniqueIdentifier
|
||||
import net.corda.core.identity.AbstractParty
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.node.services.Vault
|
||||
import net.corda.core.schemas.StatePersistable
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
|
@ -302,7 +302,7 @@ object Builder {
|
||||
|
||||
@JvmStatic
|
||||
@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
|
||||
@Deprecated("Does not support fields from a MappedSuperclass. Use equivalent on a FieldInfo.")
|
||||
|
@ -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)
|
||||
}
|
@ -18,6 +18,7 @@ import java.io.ByteArrayOutputStream
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.net.*
|
||||
import java.security.Permission
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
@ -378,6 +379,15 @@ object AttachmentURLStreamHandlerFactory : URLStreamHandlerFactory {
|
||||
private class AttachmentURLConnection(url: URL, private val attachment: Attachment) : URLConnection(url) {
|
||||
override fun getContentLengthLong(): Long = attachment.size.toLong()
|
||||
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() {
|
||||
connected = true
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ package net.corda.core.serialization.internal
|
||||
import net.corda.core.DeleteForDJVM
|
||||
import net.corda.core.DoNotImplement
|
||||
import net.corda.core.KeepForDJVM
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.serialization.*
|
||||
import net.corda.core.utilities.ByteSequence
|
||||
import net.corda.core.utilities.sequence
|
||||
|
@ -134,7 +134,7 @@ open class 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)
|
||||
fun toWireTransaction(services: ServicesForResolution): WireTransaction = toWireTransactionWithContext(services, null)
|
||||
|
@ -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.
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
fun checkSignature(sig: TransactionSignature) {
|
||||
|
@ -471,9 +471,9 @@ class CryptoUtilsTest {
|
||||
val privKeyDecoded = Crypto.decodePrivateKey(privKey.encoded)
|
||||
val pubKeyDecoded = Crypto.decodePublicKey(pubKey.encoded)
|
||||
|
||||
assertEquals(privKeyDecoded.algorithm, "ECDSA")
|
||||
assertEquals(privKeyDecoded.algorithm, "EC")
|
||||
assertEquals((privKeyDecoded as ECKey).parameters, ECNamedCurveTable.getParameterSpec("secp256k1"))
|
||||
assertEquals(pubKeyDecoded.algorithm, "ECDSA")
|
||||
assertEquals(pubKeyDecoded.algorithm, "EC")
|
||||
assertEquals((pubKeyDecoded as ECKey).parameters, ECNamedCurveTable.getParameterSpec("secp256k1"))
|
||||
}
|
||||
|
||||
@ -481,9 +481,9 @@ class CryptoUtilsTest {
|
||||
fun `ECDSA secp256r1 scheme finder by key type`() {
|
||||
val keyPairR1 = Crypto.generateKeyPair(ECDSA_SECP256R1_SHA256)
|
||||
val (privR1, pubR1) = keyPairR1
|
||||
assertEquals(privR1.algorithm, "ECDSA")
|
||||
assertEquals(privR1.algorithm, "EC")
|
||||
assertEquals((privR1 as ECKey).parameters, ECNamedCurveTable.getParameterSpec("secp256r1"))
|
||||
assertEquals(pubR1.algorithm, "ECDSA")
|
||||
assertEquals(pubR1.algorithm, "EC")
|
||||
assertEquals((pubR1 as ECKey).parameters, ECNamedCurveTable.getParameterSpec("secp256r1"))
|
||||
}
|
||||
|
||||
@ -530,11 +530,11 @@ class CryptoUtilsTest {
|
||||
val encodedPubK1 = pubK1.encoded
|
||||
|
||||
val decodedPrivK1 = Crypto.decodePrivateKey(encodedPrivK1)
|
||||
assertEquals(decodedPrivK1.algorithm, "ECDSA")
|
||||
assertEquals(decodedPrivK1.algorithm, "EC")
|
||||
assertEquals(decodedPrivK1, privK1)
|
||||
|
||||
val decodedPubK1 = Crypto.decodePublicKey(encodedPubK1)
|
||||
assertEquals(decodedPubK1.algorithm, "ECDSA")
|
||||
assertEquals(decodedPubK1.algorithm, "EC")
|
||||
assertEquals(decodedPubK1, pubK1)
|
||||
}
|
||||
|
||||
@ -546,11 +546,11 @@ class CryptoUtilsTest {
|
||||
val encodedPubR1 = pubR1.encoded
|
||||
|
||||
val decodedPrivR1 = Crypto.decodePrivateKey(encodedPrivR1)
|
||||
assertEquals(decodedPrivR1.algorithm, "ECDSA")
|
||||
assertEquals(decodedPrivR1.algorithm, "EC")
|
||||
assertEquals(decodedPrivR1, privR1)
|
||||
|
||||
val decodedPubR1 = Crypto.decodePublicKey(encodedPubR1)
|
||||
assertEquals(decodedPubR1.algorithm, "ECDSA")
|
||||
assertEquals(decodedPubR1.algorithm, "EC")
|
||||
assertEquals(decodedPubR1, pubR1)
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
@ -87,11 +87,9 @@
|
||||
<ID>ComplexCondition:InternalUtils.kt$it.type == this && it.isPublic && it.isStatic && it.isFinal</ID>
|
||||
<ID>ComplexCondition:Main.kt$Main$(hostname != null) && (port != null) && (username != null) && (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:TopLevelTransition.kt$TopLevelTransition$currentState.isTransactionTracked && checkpoint.flowState is FlowState.Started && checkpoint.flowState.flowIORequest is FlowIORequest.WaitForLedgerCommit && checkpoint.flowState.flowIORequest.hash == event.transaction.id</ID>
|
||||
<ID>ComplexCondition:WireTransaction.kt$WireTransaction$notary != null && (inputs.isNotEmpty() || references.isNotEmpty() || timeWindow != null)</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: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: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>
|
||||
@ -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<*>.toConfigIterable(field: Field): Iterable<Any?></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<String, Any></ID>
|
||||
<ID>ComplexMethod:ConfigUtilities.kt$private fun Config.getCollectionValue(path: String, type: KType, onUnknownKeys: (Set<String>, logger: Logger) -> Unit, nestedPath: String?, baseDirectory: Path?): Collection<Any></ID>
|
||||
<ID>ComplexMethod:ConfigUtilities.kt$private fun Config.getSingleValue(path: String, type: KType, onUnknownKeys: (Set<String>, logger: Logger) -> Unit, nestedPath: String?, baseDirectory: Path?): 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 onDelivery(event: Event)</ID>
|
||||
<ID>ComplexMethod:ConstraintsUtils.kt$ fun AttachmentConstraint.canBeTransitionedFrom(input: AttachmentConstraint, attachment: ContractAttachment): Boolean</ID>
|
||||
<ID>ComplexMethod:CordaCliWrapper.kt$fun CordaCliWrapper.start(args: Array<String>)</ID>
|
||||
<ID>ComplexMethod:CordaPersistence.kt$CordaPersistence$private fun <T> inTopLevelTransaction(isolationLevel: TransactionIsolationLevel, recoverableFailureTolerance: Int, recoverAnyNestedSQLException: Boolean, statement: DatabaseTransaction.() -> T): T</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>
|
||||
@ -164,7 +159,6 @@
|
||||
<ID>ComplexMethod:MerkleTransaction.kt$FilteredTransaction.Companion$ private fun filterWithFun(wtx: WireTransaction, filtering: Predicate<Any>): List<FilteredComponentGroup></ID>
|
||||
<ID>ComplexMethod:NetworkBootstrapper.kt$NetworkBootstrapper$private fun bootstrap( directory: Path, cordappJars: List<Path>, copyCordapps: CopyCordapps, fromCordform: Boolean, networkParametersOverrides: NetworkParametersOverrides = NetworkParametersOverrides() )</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:NetworkRegistrationHelper.kt$NetworkRegistrationHelper$ private fun pollServerForCertificates(requestId: String): List<X509Certificate></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<out Any?>?): Any?</ID>
|
||||
<ID>ComplexMethod:RPCClientProxyHandler.kt$RPCClientProxyHandler$private fun attemptReconnect()</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<out Any>?, 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: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>
|
||||
@ -215,7 +208,6 @@
|
||||
<ID>EmptyDefaultConstructor:FlowRetryTest.kt$AsyncRetryFlow$()</ID>
|
||||
<ID>EmptyDefaultConstructor:FlowRetryTest.kt$RetryFlow$()</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:InMemoryIdentityService.kt$InMemoryIdentityService${ }</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 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: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: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.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: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>
|
||||
@ -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: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: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$InterestRateSwap$// TODO: Confirm: would someone really enter a swap with a negative fixed rate?</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: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: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: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>
|
||||
@ -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_value()</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<S> : SingletonSerializeAsToken</ID>
|
||||
<ID>LargeClass:SingleThreadedStateMachineManager.kt$SingleThreadedStateMachineManager : StateMachineManagerStateMachineManagerInternal</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) -> Party?, wellKnownPartyFromAnonymous: (AbstractParty) -> Party?, schemaService: SchemaService, hikariProperties: Properties, cacheFactory: NamedCacheFactory, customClassLoader: ClassLoader?)</ID>
|
||||
<ID>LongParameterList:AbstractNode.kt$(hikariProperties: Properties, databaseConfig: DatabaseConfig, schemas: Set<MappedSchema>, 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: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, 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, 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, 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:AttachmentsClassLoader.kt$AttachmentsClassLoaderBuilder$(attachments: List<Attachment>, params: NetworkParameters, txId: SecureHash, isAttachmentTrusted: (Attachment) -> Boolean, parent: ClassLoader = ClassLoader.getSystemClassLoader(), block: (ClassLoader) -> T)</ID>
|
||||
<ID>LongParameterList:BFTSmart.kt$BFTSmart.Replica$( states: List<StateRef>, txId: SecureHash, callerName: CordaX500Name, requestSignature: NotarisationRequestSignature, timeWindow: TimeWindow?, references: List<StateRef> = 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.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$(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<ContractClassName> = emptySet(), uploader: String? = null, signerKeys: List<PublicKey> = 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: 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:JarScanningCordappLoader.kt$CordappLoaderTemplate$36</ID>
|
||||
<ID>MagicNumber:JarScanningCordappLoader.kt$CordappLoaderTemplate$64</ID>
|
||||
<ID>MagicNumber:JarScanningCordappLoader.kt$JarScanningCordappLoader$1000</ID>
|
||||
<ID>MagicNumber:JarSignatureCollector.kt$JarSignatureCollector$1024</ID>
|
||||
<ID>MagicNumber:JarSignatureTestUtils.kt$JarSignatureTestUtils$14</ID>
|
||||
<ID>MagicNumber:KMSUtils.kt$3650</ID>
|
||||
@ -1207,7 +1193,6 @@
|
||||
<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: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:FinanceWorkflowsUtils.kt$net.corda.finance.workflows.utils.FinanceWorkflowsUtils.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:ReceiveAllFlowTests.kt$net.corda.coretests.flows.ReceiveAllFlowTests.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:SampleData.kt$net.corda.deterministic.verifier.SampleData.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:ShutdownManager.kt$ShutdownManager$fun shutdown()</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<SessionId, SerializedBytes<Any>>)</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:TransactionVerifierServiceInternal.kt$Verifier$ private fun verifyConstraintsValidity(contractAttachmentsByContract: Map<ContractClassName, ContractAttachment>)</ID>
|
||||
@ -1316,7 +1298,6 @@
|
||||
<ID>SpreadOperator:ConfigUtilities.kt$(*pairs)</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:CordaCliWrapper.kt$(RunLast().useOut(System.out).useAnsi(defaultAnsiMode), DefaultExceptionHandler<List<Any>>().useErr(System.err).useAnsi(defaultAnsiMode).andExit(ExitCodes.FAILURE), *args)</ID>
|
||||
<ID>SpreadOperator:CordaRPCOpsImpl.kt$CordaRPCOpsImpl$(logicType, context(), *args)</ID>
|
||||
<ID>SpreadOperator:CordaX500Name.kt$CordaX500Name.Companion$(*Locale.getISOCountries(), unspecifiedCountry)</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: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: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, cause.staticLocationBasedHash(hashedFields, visited + cause))</ID>
|
||||
<ID>SpreadOperator:ExceptionsErrorCodeFunctions.kt$(*hashedFields.invoke(this))</ID>
|
||||
@ -1483,6 +1463,7 @@
|
||||
<ID>TooGenericExceptionCaught:AbstractNode.kt$ex: Exception</ID>
|
||||
<ID>TooGenericExceptionCaught:AbstractNodeTests.kt$ColdJVM.Companion$t: Throwable</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:AttachmentLoadingTests.kt$AttachmentLoadingTests.ConsumeAndBroadcastResponderFlow$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:BlobInspector.kt$BlobInspector$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:CertRole.kt$CertRole.Companion$ex: ArrayIndexOutOfBoundsException</ID>
|
||||
<ID>TooGenericExceptionCaught:CheckpointAgent.kt$CheckpointAgent.Companion$e: Exception</ID>
|
||||
@ -1506,9 +1486,6 @@
|
||||
<ID>TooGenericExceptionCaught:ContractUpgradeTransactions.kt$ContractUpgradeWireTransaction$e: Exception</ID>
|
||||
<ID>TooGenericExceptionCaught:CordaAuthenticationPlugin.kt$CordaAuthenticationPlugin$e: 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:CordaRPCClientTest.kt$CordaRPCClientTest$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:DockerInstantiator.kt$DockerInstantiator$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:DriverTests.kt$DriverTests$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:ReconnectingObservable.kt$ReconnectingObservable.ReconnectingSubscriber$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:SerializationOutputTests.kt$SerializationOutputTests$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:VaultStateMigration.kt$VaultStateIterator$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$e: 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:Currencies.kt$net.corda.finance.Currencies.kt</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:FlowLogic.kt$FlowLogic<out T></ID>
|
||||
<ID>TooManyFunctions:FlowStateMachineImpl.kt$FlowStateMachineImpl<R> : FiberFlowStateMachineFlowFiber</ID>
|
||||
@ -1761,6 +1737,18 @@
|
||||
<ID>TopLevelPropertyNaming:SerializationEnvironment.kt$val _inheritableContextSerializationEnv = InheritableThreadLocalToggleField<SerializationEnvironment>("inheritableContextSerializationEnv") { stack -> stack.fold(false) { isAGlobalThreadBeingCreated, e -> isAGlobalThreadBeingCreated || (e.className == "io.netty.util.concurrent.GlobalEventExecutor" && e.methodName == "startThread") || (e.className == "java.util.concurrent.ForkJoinPool\$DefaultForkJoinWorkerThreadFactory" && e.methodName == "newThread") } }</ID>
|
||||
<ID>TopLevelPropertyNaming:SerializationEnvironment.kt$val _rpcClientSerializationEnv = SimpleToggleField<SerializationEnvironment>("rpcClientSerializationEnv")</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 MEGA_CORP = TestIdentity(CordaX500Name("MegaCorp", "London", "GB")).party</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: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: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:AMQPSerializationScheme.kt$import net.corda.core.serialization.*</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.identity.*</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:CordaRPCOpsImplTest.kt$import net.corda.core.messaging.*</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.nodeapi.internal.persistence.*</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:DemoBench.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.internal.*</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:GenericsTests.kt$import net.corda.serialization.internal.amqp.testutils.*</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.node.services.config.*</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: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:JVMConfig.kt$import tornadofx.*</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:Kryo.kt$import com.esotericsoftware.kryo.*</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:KryoTests.kt$import kotlin.test.*</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:NetworkBootstrapperRunnerTests.kt$import org.junit.*</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:NetworkIdentityModel.kt$import net.corda.client.jfx.utils.*</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.testing.core.*</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:NetworkParametersReaderTest.kt$import net.corda.core.internal.*</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.nio.file.*</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:PersistentStateServiceTests.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:RPCMultipleInterfacesTests.kt$import org.junit.Assert.*</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.testing.node.internal.*</ID>
|
||||
<ID>WildcardImport:ReceiveTransactionFlow.kt$import net.corda.core.contracts.*</ID>
|
||||
@ -2427,7 +2391,6 @@
|
||||
<ID>WildcardImport:SearchField.kt$import tornadofx.*</ID>
|
||||
<ID>WildcardImport:SecureHashTest.kt$import org.junit.Assert.*</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 net.corda.core.serialization.*</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.internal.vault.*</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:WhitelistBasedTypeModelConfiguration.kt$import org.apache.qpid.proton.amqp.*</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:WithFinality.kt$import net.corda.core.flows.*</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:X509CertificateSerializer.kt$import net.corda.serialization.internal.amqp.*</ID>
|
||||
<ID>WildcardImport:X509EdDSAEngine.kt$import java.security.*</ID>
|
||||
|
@ -193,4 +193,6 @@ style:
|
||||
WildcardImport:
|
||||
active: true
|
||||
excludes: "**/buildSrc/**"
|
||||
excludeImports: 'java.util.*,kotlinx.android.synthetic.*'
|
||||
excludeImports: 'java.util.*,kotlinx.android.synthetic.*'
|
||||
UnusedImports:
|
||||
active: true
|
62
docker/src/docker/Dockerfile-debug
Normal file
62
docker/src/docker/Dockerfile-debug
Normal 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"]
|
66
docker/src/docker/DockerfileAL-debug
Normal file
66
docker/src/docker/DockerfileAL-debug
Normal 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"]
|
@ -11,7 +11,6 @@ import net.corda.common.validation.internal.Validated
|
||||
import net.corda.node.services.config.NodeConfiguration
|
||||
import net.corda.node.services.config.parseAsNodeConfiguration
|
||||
import net.corda.nodeapi.internal.config.toConfig
|
||||
import net.corda.nodeapi.internal.config.toConfigValue
|
||||
import java.io.File
|
||||
|
||||
class ConfigExporter {
|
||||
|
197
docs/Makefile
197
docs/Makefile
@ -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
|
@ -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:
|
||||
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**)
|
||||
[corda/corda-docs](https://github.com/corda/corda-docs)
|
||||
|
||||
## Build process
|
||||
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`)
|
||||
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.
|
||||
|
||||
**Windows users:** *If this task fails because Docker can't find make-docsite.sh, go to Settings > Shared Drives in the Docker system tray
|
||||
agent, make sure the relevant drive is shared, and click 'Reset credentials'.*
|
||||
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`).
|
||||
|
||||
# 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
|
||||
"|corda_version|"
|
||||
"|corda_version_lower|"
|
||||
"|java_version|"
|
||||
"|kotlin_version|"
|
||||
"|gradle_plugins_version|"
|
||||
"|quasar_version|"
|
||||
```
|
||||
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).
|
||||
|
||||
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`
|
||||
(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.
|
||||
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).
|
||||
|
@ -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
|
@ -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
|
1
docs/docs_builder/lexer-fix/.gitignore
vendored
1
docs/docs_builder/lexer-fix/.gitignore
vendored
@ -1 +0,0 @@
|
||||
Pygments*.whl
|
@ -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
@ -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`)
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user