Merge branch 'release/os/4.3' into release/os/4.4

# Conflicts:
#	docs/source/docker-image.rst
This commit is contained in:
Anthony Keenan 2019-10-21 13:44:32 +01:00
commit 0a194cfb51
91 changed files with 1787 additions and 913 deletions

View File

@ -29,20 +29,18 @@ pipeline {
} }
} }
stage('Corda Pull Request - Run Tests') { stage('Unit Tests') {
stage('Unit Tests') { steps {
steps { sh "./gradlew " +
sh "./gradlew " + "-DbuildId=\"\${BUILD_ID}\" " +
"-DbuildId=\"\${BUILD_ID}\" " + "-Dkubenetize=true " +
"-Dkubenetize=true " + "-Ddocker.tag=\"\${DOCKER_TAG_TO_USE}\"" +
"-Ddocker.tag=\"\${DOCKER_TAG_TO_USE}\"" + " allParallelUnitTest"
" allParallelUnitTest" if (env.CHANGE_ID) {
if (env.CHANGE_ID) { pullRequest.createStatus(status: 'success',
pullRequest.createStatus(status: 'success', context: 'continuous-integration/jenkins/pr-merge/unitTest',
context: 'continuous-integration/jenkins/pr-merge/unitTest', description: 'Unit Tests Passed',
description: 'Unit Tests Passed', targetUrl: "${env.JOB_URL}/testResults")
targetUrl: "${env.JOB_URL}/testResults")
}
} }
} }
} }

View File

@ -3,6 +3,14 @@
<option name="LINE_SEPARATOR" value="&#10;" /> <option name="LINE_SEPARATOR" value="&#10;" />
<option name="RIGHT_MARGIN" value="140" /> <option name="RIGHT_MARGIN" value="140" />
<option name="SOFT_MARGINS" value="140" /> <option name="SOFT_MARGINS" value="140" />
<JavaCodeStyleSettings>
<option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="999" />
<option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="999" />
</JavaCodeStyleSettings>
<GroovyCodeStyleSettings>
<option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="100" />
<option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="100" />
</GroovyCodeStyleSettings>
<JetCodeStyleSettings> <JetCodeStyleSettings>
<option name="PACKAGES_TO_USE_STAR_IMPORTS"> <option name="PACKAGES_TO_USE_STAR_IMPORTS">
<value> <value>
@ -11,6 +19,8 @@
<package name="tornadofx" withSubpackages="false" static="false" /> <package name="tornadofx" withSubpackages="false" static="false" />
</value> </value>
</option> </option>
<option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="2147483647" />
<option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="2147483647" />
<option name="CONTINUATION_INDENT_IN_PARAMETER_LISTS" value="true" /> <option name="CONTINUATION_INDENT_IN_PARAMETER_LISTS" value="true" />
<option name="CONTINUATION_INDENT_IN_ARGUMENT_LISTS" value="true" /> <option name="CONTINUATION_INDENT_IN_ARGUMENT_LISTS" value="true" />
<option name="CONTINUATION_INDENT_FOR_EXPRESSION_BODIES" value="true" /> <option name="CONTINUATION_INDENT_FOR_EXPRESSION_BODIES" value="true" />

View File

@ -1,6 +1,5 @@
<component name="ProjectCodeStyleConfiguration"> <component name="ProjectCodeStyleConfiguration">
<state> <state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" /> <option name="USE_PER_PROJECT_SETTINGS" value="true" />
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
</state> </state>
</component> </component>

3
Jenkinsfile vendored
View File

@ -1,3 +1,4 @@
import static com.r3.build.BuildControl.killAllExistingBuildsForJob
@Library('existing-build-control') @Library('existing-build-control')
import static com.r3.build.BuildControl.killAllExistingBuildsForJob import static com.r3.build.BuildControl.killAllExistingBuildsForJob
@ -8,7 +9,7 @@ pipeline {
options { timestamps() } options { timestamps() }
environment { environment {
DOCKER_TAG_TO_USE = "${UUID.randomUUID().toString().toLowerCase().subSequence(0, 12)}" DOCKER_TAG_TO_USE = "${env.GIT_COMMIT.subSequence(0, 8)}"
EXECUTOR_NUMBER = "${env.EXECUTOR_NUMBER}" EXECUTOR_NUMBER = "${env.EXECUTOR_NUMBER}"
BUILD_ID = "${env.BUILD_ID}-${env.JOB_NAME}" BUILD_ID = "${env.BUILD_ID}-${env.JOB_NAME}"
} }

View File

@ -448,7 +448,7 @@ task detektBaseline(type: JavaExec) {
main = "io.gitlab.arturbosch.detekt.cli.Main" main = "io.gitlab.arturbosch.detekt.cli.Main"
classpath = configurations.detekt classpath = configurations.detekt
def input = "$projectDir" def input = "$projectDir"
def config = "$projectDir/detekt-config.yml" def config = "$projectDir/detekt-config.yml, $projectDir/detekt-baseline-config.yml"
def baseline = "$projectDir/detekt-baseline.xml" def baseline = "$projectDir/detekt-baseline.xml"
def params = ['-i', input, '-c', config, '-b', baseline, '--create-baseline'] def params = ['-i', input, '-c', config, '-b', baseline, '--create-baseline']
args(params) args(params)
@ -612,6 +612,14 @@ task allParallelSlowIntegrationTest(type: ParallelTestGroup) {
memoryInGbPerFork 10 memoryInGbPerFork 10
distribute Distribution.CLASS distribute Distribution.CLASS
} }
task allParallelSmokeTest(type: ParallelTestGroup) {
testGroups "smokeTest"
numberOfShards 4
streamOutput true
coresPerFork 6
memoryInGbPerFork 10
distribute Distribution.METHOD
}
task allParallelUnitTest(type: ParallelTestGroup) { task allParallelUnitTest(type: ParallelTestGroup) {
testGroups "test" testGroups "test"
numberOfShards 10 numberOfShards 10
@ -623,7 +631,15 @@ task allParallelUnitTest(type: ParallelTestGroup) {
task allParallelUnitAndIntegrationTest(type: ParallelTestGroup) { task allParallelUnitAndIntegrationTest(type: ParallelTestGroup) {
testGroups "test", "integrationTest" testGroups "test", "integrationTest"
numberOfShards 15 numberOfShards 15
streamOutput true streamOutput false
coresPerFork 6
memoryInGbPerFork 10
distribute Distribution.CLASS
}
task parallelRegressionTest(type: ParallelTestGroup) {
testGroups "test", "integrationTest", "slowIntegrationTest", "smokeTest"
numberOfShards 5
streamOutput false
coresPerFork 6 coresPerFork 6
memoryInGbPerFork 10 memoryInGbPerFork 10
distribute Distribution.CLASS distribute Distribution.CLASS

View File

@ -51,13 +51,12 @@ public class BucketingAllocator {
System.out.println("Number of tests: " + container.testsForFork.stream().mapToInt(b -> b.foundTests.size()).sum()); System.out.println("Number of tests: " + container.testsForFork.stream().mapToInt(b -> b.foundTests.size()).sum());
System.out.println("Tests to Run: "); System.out.println("Tests to Run: ");
container.testsForFork.forEach(tb -> { container.testsForFork.forEach(tb -> {
System.out.println(tb.nameWithAsterix); System.out.println(tb.testName);
tb.foundTests.forEach(ft -> System.out.println("\t" + ft.getFirst() + ", " + ft.getSecond())); tb.foundTests.forEach(ft -> System.out.println("\t" + ft.getFirst() + ", " + ft.getSecond()));
}); });
}); });
} }
private void allocateTestsToForks(@NotNull List<TestBucket> matchedTests) { private void allocateTestsToForks(@NotNull List<TestBucket> matchedTests) {
matchedTests.forEach(matchedTestBucket -> { matchedTests.forEach(matchedTestBucket -> {
TestsForForkContainer smallestContainer = Collections.min(forkContainers, Comparator.comparing(TestsForForkContainer::getCurrentDuration)); TestsForForkContainer smallestContainer = Collections.min(forkContainers, Comparator.comparing(TestsForForkContainer::getCurrentDuration));
@ -69,10 +68,9 @@ public class BucketingAllocator {
return allDiscoveredTests.stream().map(tuple -> { return allDiscoveredTests.stream().map(tuple -> {
String testName = tuple.getFirst(); String testName = tuple.getFirst();
Object task = tuple.getSecond(); Object task = tuple.getSecond();
String noAsterixName = testName.substring(0, testName.length() - 1);
//2DO [can this filtering algorithm be improved - the test names are sorted, it should be possible to do something using binary search] //2DO [can this filtering algorithm be improved - the test names are sorted, it should be possible to do something using binary search]
List<Tuple2<String, Double>> matchingTests = allTestsFromCSV.stream().filter(testFromCSV -> testFromCSV.getFirst().startsWith(noAsterixName)).collect(Collectors.toList()); List<Tuple2<String, Double>> matchingTests = allTestsFromCSV.stream().filter(testFromCSV -> testFromCSV.getFirst().startsWith(testName)).collect(Collectors.toList());
return new TestBucket(task, testName, noAsterixName, matchingTests); return new TestBucket(task, testName, matchingTests);
}).sorted(Comparator.comparing(TestBucket::getDuration).reversed()).collect(Collectors.toList()); }).sorted(Comparator.comparing(TestBucket::getDuration).reversed()).collect(Collectors.toList());
} }
@ -81,22 +79,20 @@ public class BucketingAllocator {
TestLister lister = source.getFirst(); TestLister lister = source.getFirst();
Object testTask = source.getSecond(); Object testTask = source.getSecond();
return lister.getAllTestsDiscovered().stream().map(test -> new Tuple2<>(test, testTask)).collect(Collectors.toList()); return lister.getAllTestsDiscovered().stream().map(test -> new Tuple2<>(test, testTask)).collect(Collectors.toList());
}).flatMap(Collection::stream).collect(Collectors.toList()); }).flatMap(Collection::stream).sorted(Comparator.comparing(Tuple2::getFirst)).collect(Collectors.toList());
} }
public static class TestBucket { public static class TestBucket {
final Object testTask; final Object testTask;
final String nameWithAsterix; final String testName;
final String nameWithoutAsterix;
final List<Tuple2<String, Double>> foundTests; final List<Tuple2<String, Double>> foundTests;
final Double duration; final Double duration;
public TestBucket(Object testTask, String nameWithAsterix, String nameWithoutAsterix, List<Tuple2<String, Double>> foundTests) { public TestBucket(Object testTask, String testName, List<Tuple2<String, Double>> foundTests) {
this.testTask = testTask; this.testTask = testTask;
this.nameWithAsterix = nameWithAsterix; this.testName = testName;
this.nameWithoutAsterix = nameWithoutAsterix;
this.foundTests = foundTests; this.foundTests = foundTests;
duration = Math.max(foundTests.stream().mapToDouble(tp -> Math.max(tp.getSecond(), 10)).sum(), 10); duration = Math.max(foundTests.stream().mapToDouble(tp -> Math.max(tp.getSecond(), 1)).sum(), 1);
} }
public Double getDuration() { public Double getDuration() {
@ -107,8 +103,7 @@ public class BucketingAllocator {
public String toString() { public String toString() {
return "TestBucket{" + return "TestBucket{" +
"testTask=" + testTask + "testTask=" + testTask +
", nameWithAsterix='" + nameWithAsterix + '\'' + ", nameWithAsterix='" + testName + '\'' +
", nameWithoutAsterix='" + nameWithoutAsterix + '\'' +
", foundTests=" + foundTests + ", foundTests=" + foundTests +
", duration=" + duration + ", duration=" + duration +
'}'; '}';
@ -142,7 +137,7 @@ public class BucketingAllocator {
} }
public List<String> getTestsForTask(Object task) { public List<String> getTestsForTask(Object task) {
return frozenTests.getOrDefault(task, Collections.emptyList()).stream().map(it -> it.nameWithAsterix).collect(Collectors.toList()); return frozenTests.getOrDefault(task, Collections.emptyList()).stream().map(it -> it.testName).collect(Collectors.toList());
} }
public List<TestBucket> getBucketsForFork() { public List<TestBucket> getBucketsForFork() {

View File

@ -41,8 +41,8 @@ public class BucketingAllocatorTask extends DefaultTask {
this.dependsOn(source); this.dependsOn(source);
} }
public List<String> getTestsForForkAndTestTask(Integer fork, Test testTask) { public List<String> getTestIncludesForForkAndTestTask(Integer fork, Test testTask) {
return allocator.getTestsForForkAndTestTask(fork, testTask); return allocator.getTestsForForkAndTestTask(fork, testTask).stream().map(t -> t + "*").collect(Collectors.toList());
} }
@TaskAction @TaskAction
@ -56,11 +56,11 @@ public class BucketingAllocatorTask extends DefaultTask {
String duration = "Duration(ms)"; String duration = "Duration(ms)";
List<CSVRecord> records = CSVFormat.DEFAULT.withHeader().parse(reader).getRecords(); List<CSVRecord> records = CSVFormat.DEFAULT.withHeader().parse(reader).getRecords();
return records.stream().map(record -> { return records.stream().map(record -> {
try{ try {
String testName = record.get(name); String testName = record.get(name);
String testDuration = record.get(duration); String testDuration = record.get(duration);
return new Tuple2<>(testName, Math.max(Double.parseDouble(testDuration), 10)); return new Tuple2<>(testName, Math.max(Double.parseDouble(testDuration), 1));
}catch (IllegalArgumentException | IllegalStateException e){ } catch (IllegalArgumentException | IllegalStateException e) {
return null; return null;
} }
}).filter(Objects::nonNull).sorted(Comparator.comparing(Tuple2::getFirst)).collect(Collectors.toList()); }).filter(Objects::nonNull).sorted(Comparator.comparing(Tuple2::getFirst)).collect(Collectors.toList());

View File

@ -10,7 +10,6 @@ import org.gradle.api.tasks.testing.Test
*/ */
class DistributedTesting implements Plugin<Project> { class DistributedTesting implements Plugin<Project> {
static def getPropertyAsInt(Project proj, String property, Integer defaultValue) { static def getPropertyAsInt(Project proj, String property, Integer defaultValue) {
return proj.hasProperty(property) ? Integer.parseInt(proj.property(property).toString()) : defaultValue return proj.hasProperty(property) ? Integer.parseInt(proj.property(property).toString()) : defaultValue
} }
@ -33,6 +32,7 @@ class DistributedTesting implements Plugin<Project> {
//1. add the task to determine all tests within the module //1. add the task to determine all tests within the module
//2. modify the underlying testing task to use the output of the listing task to include a subset of tests for each fork //2. modify the underlying testing task to use the output of the listing task to include a subset of tests for each fork
//3. KubesTest will invoke these test tasks in a parallel fashion on a remote k8s cluster //3. KubesTest will invoke these test tasks in a parallel fashion on a remote k8s cluster
//4. after each completed test write its name to a file to keep track of what finished for restart purposes
project.subprojects { Project subProject -> project.subprojects { Project subProject ->
subProject.tasks.withType(Test) { Test task -> subProject.tasks.withType(Test) { Test task ->
println "Evaluating ${task.getPath()}" println "Evaluating ${task.getPath()}"
@ -47,7 +47,6 @@ class DistributedTesting implements Plugin<Project> {
if (!task.hasProperty("ignoreForDistribution")) { if (!task.hasProperty("ignoreForDistribution")) {
KubesTest parallelTestTask = generateParallelTestingTask(subProject, task, imageBuildingTask, providedTag) KubesTest parallelTestTask = generateParallelTestingTask(subProject, task, imageBuildingTask, providedTag)
} }
} }
} }
@ -129,9 +128,23 @@ class DistributedTesting implements Plugin<Project> {
maxHeapSize = "6g" maxHeapSize = "6g"
doFirst { doFirst {
filter { filter {
List<String> executedTests = []
File executedTestsFile = new File(KubesTest.TEST_RUN_DIR + "/executedTests.txt")
try {
executedTests = executedTestsFile.readLines()
} catch (FileNotFoundException e) {
executedTestsFile.createNewFile()
}
task.afterTest { desc, result ->
executedTestsFile.withWriterAppend { writer ->
writer.writeLine(desc.getClassName() + "." + desc.getName())
}
}
def fork = getPropertyAsInt(subProject, "dockerFork", 0) def fork = getPropertyAsInt(subProject, "dockerFork", 0)
subProject.logger.info("requesting tests to include in testing task ${task.getPath()} (idx: ${fork})") subProject.logger.info("requesting tests to include in testing task ${task.getPath()} (idx: ${fork})")
List<String> includes = globalAllocator.getTestsForForkAndTestTask( List<String> includes = globalAllocator.getTestIncludesForForkAndTestTask(
fork, fork,
task) task)
subProject.logger.info "got ${includes.size()} tests to include into testing task ${task.getPath()}" subProject.logger.info "got ${includes.size()} tests to include into testing task ${task.getPath()}"
@ -141,10 +154,18 @@ class DistributedTesting implements Plugin<Project> {
excludeTestsMatching "*" excludeTestsMatching "*"
} }
includes.removeAll(executedTests)
executedTests.forEach { exclude ->
subProject.logger.info "excluding: $exclude for testing task ${task.getPath()}"
excludeTestsMatching exclude
}
includes.forEach { include -> includes.forEach { include ->
subProject.logger.info "including: $include for testing task ${task.getPath()}" subProject.logger.info "including: $include for testing task ${task.getPath()}"
includeTestsMatching include includeTestsMatching include
} }
failOnNoMatchingTests false failOnNoMatchingTests false
} }
} }

View File

@ -1,123 +0,0 @@
package net.corda.testing
import com.bmuschko.gradle.docker.DockerRegistryCredentials
import com.bmuschko.gradle.docker.tasks.container.*
import com.bmuschko.gradle.docker.tasks.image.*
import org.gradle.api.GradleException
import org.gradle.api.Plugin
import org.gradle.api.Project
/**
this plugin is responsible for setting up all the required docker image building tasks required for producing and pushing an
image of the current build output to a remote container registry
*/
class ImageBuilding implements Plugin<Project> {
public static final String registryName = "stefanotestingcr.azurecr.io/testing"
DockerPushImage pushTask
@Override
void apply(Project project) {
def registryCredentialsForPush = new DockerRegistryCredentials(project.getObjects())
registryCredentialsForPush.username.set("stefanotestingcr")
registryCredentialsForPush.password.set(System.getProperty("docker.push.password") ? System.getProperty("docker.push.password") : "")
DockerPullImage pullTask = project.tasks.create("pullBaseImage", DockerPullImage) {
repository = "stefanotestingcr.azurecr.io/buildbase"
tag = "latest"
doFirst {
registryCredentials = registryCredentialsForPush
}
}
DockerBuildImage buildDockerImageForSource = project.tasks.create('buildDockerImageForSource', DockerBuildImage) {
dependsOn Arrays.asList(project.rootProject.getTasksByName("clean", true), pullTask)
inputDir.set(new File("."))
dockerFile.set(new File(new File("testing"), "Dockerfile"))
}
DockerCreateContainer createBuildContainer = project.tasks.create('createBuildContainer', DockerCreateContainer) {
File baseWorkingDir = new File(System.getProperty("docker.work.dir") ? System.getProperty("docker.work.dir") : System.getProperty("java.io.tmpdir"))
File gradleDir = new File(baseWorkingDir, "gradle")
File mavenDir = new File(baseWorkingDir, "maven")
doFirst {
if (!gradleDir.exists()) {
gradleDir.mkdirs()
}
if (!mavenDir.exists()) {
mavenDir.mkdirs()
}
logger.info("Will use: ${gradleDir.absolutePath} for caching gradle artifacts")
}
dependsOn buildDockerImageForSource
targetImageId buildDockerImageForSource.getImageId()
binds = [(gradleDir.absolutePath): "/tmp/gradle", (mavenDir.absolutePath): "/home/root/.m2"]
}
DockerStartContainer startBuildContainer = project.tasks.create('startBuildContainer', DockerStartContainer) {
dependsOn createBuildContainer
targetContainerId createBuildContainer.getContainerId()
}
DockerLogsContainer logBuildContainer = project.tasks.create('logBuildContainer', DockerLogsContainer) {
dependsOn startBuildContainer
targetContainerId createBuildContainer.getContainerId()
follow = true
}
DockerWaitContainer waitForBuildContainer = project.tasks.create('waitForBuildContainer', DockerWaitContainer) {
dependsOn logBuildContainer
targetContainerId createBuildContainer.getContainerId()
doLast {
if (getExitCode() != 0) {
throw new GradleException("Failed to build docker image, aborting build")
}
}
}
DockerCommitImage commitBuildImageResult = project.tasks.create('commitBuildImageResult', DockerCommitImage) {
dependsOn waitForBuildContainer
targetContainerId createBuildContainer.getContainerId()
}
DockerTagImage tagBuildImageResult = project.tasks.create('tagBuildImageResult', DockerTagImage) {
dependsOn commitBuildImageResult
imageId = commitBuildImageResult.getImageId()
tag = System.getProperty("docker.provided.tag") ? System.getProperty("docker.provided.tag") : "${UUID.randomUUID().toString().toLowerCase().subSequence(0, 12)}"
repository = registryName
}
DockerPushImage pushBuildImage = project.tasks.create('pushBuildImage', DockerPushImage) {
dependsOn tagBuildImageResult
doFirst {
registryCredentials = registryCredentialsForPush
}
imageName = registryName
tag = tagBuildImageResult.tag
}
this.pushTask = pushBuildImage
DockerRemoveContainer deleteContainer = project.tasks.create('deleteBuildContainer', DockerRemoveContainer) {
dependsOn pushBuildImage
targetContainerId createBuildContainer.getContainerId()
}
DockerRemoveImage deleteTaggedImage = project.tasks.create('deleteTaggedImage', DockerRemoveImage) {
dependsOn pushBuildImage
force = true
targetImageId commitBuildImageResult.getImageId()
}
DockerRemoveImage deleteBuildImage = project.tasks.create('deleteBuildImage', DockerRemoveImage) {
dependsOn deleteContainer, deleteTaggedImage
force = true
targetImageId buildDockerImageForSource.getImageId()
}
if (System.getProperty("docker.keep.image") == null) {
pushBuildImage.finalizedBy(deleteContainer, deleteBuildImage, deleteTaggedImage)
}
}
}

View File

@ -0,0 +1,147 @@
package net.corda.testing;
import com.bmuschko.gradle.docker.DockerRegistryCredentials;
import com.bmuschko.gradle.docker.tasks.container.*;
import com.bmuschko.gradle.docker.tasks.image.*;
import org.gradle.api.GradleException;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/**
* this plugin is responsible for setting up all the required docker image building tasks required for producing and pushing an
* image of the current build output to a remote container registry
*/
public class ImageBuilding implements Plugin<Project> {
public static final String registryName = "stefanotestingcr.azurecr.io/testing";
public DockerPushImage pushTask;
@Override
public void apply(@NotNull final Project project) {
final DockerRegistryCredentials registryCredentialsForPush = new DockerRegistryCredentials(project.getObjects());
registryCredentialsForPush.getUsername().set("stefanotestingcr");
registryCredentialsForPush.getPassword().set(System.getProperty("docker.push.password", ""));
final DockerPullImage pullTask = project.getTasks().create("pullBaseImage", DockerPullImage.class, dockerPullImage -> {
dockerPullImage.doFirst(task -> dockerPullImage.setRegistryCredentials(registryCredentialsForPush));
dockerPullImage.getRepository().set("stefanotestingcr.azurecr.io/buildbase");
dockerPullImage.getTag().set("latest");
});
final DockerBuildImage buildDockerImageForSource = project.getTasks().create("buildDockerImageForSource", DockerBuildImage.class,
dockerBuildImage -> {
dockerBuildImage.dependsOn(Arrays.asList(project.getRootProject().getTasksByName("clean", true), pullTask));
dockerBuildImage.getInputDir().set(new File("."));
dockerBuildImage.getDockerFile().set(new File(new File("testing"), "Dockerfile"));
});
final DockerCreateContainer createBuildContainer = project.getTasks().create("createBuildContainer", DockerCreateContainer.class,
dockerCreateContainer -> {
final File baseWorkingDir = new File(System.getProperty("docker.work.dir") != null &&
!System.getProperty("docker.work.dir").isEmpty() ?
System.getProperty("docker.work.dir") : System.getProperty("java.io.tmpdir"));
final File gradleDir = new File(baseWorkingDir, "gradle");
final File mavenDir = new File(baseWorkingDir, "maven");
dockerCreateContainer.doFirst(task -> {
if (!gradleDir.exists()) {
gradleDir.mkdirs();
}
if (!mavenDir.exists()) {
mavenDir.mkdirs();
}
project.getLogger().info("Will use: ${gradleDir.absolutePath} for caching gradle artifacts");
});
dockerCreateContainer.dependsOn(buildDockerImageForSource);
dockerCreateContainer.targetImageId(buildDockerImageForSource.getImageId());
final Map<String, String> map = new HashMap<>();
map.put(gradleDir.getAbsolutePath(), "/tmp/gradle");
map.put(mavenDir.getAbsolutePath(), "/home/root/.m2");
dockerCreateContainer.getBinds().set(map);
});
final DockerStartContainer startBuildContainer = project.getTasks().create("startBuildContainer", DockerStartContainer.class,
dockerStartContainer -> {
dockerStartContainer.dependsOn(createBuildContainer);
dockerStartContainer.targetContainerId(createBuildContainer.getContainerId());
});
final DockerLogsContainer logBuildContainer = project.getTasks().create("logBuildContainer", DockerLogsContainer.class,
dockerLogsContainer -> {
dockerLogsContainer.dependsOn(startBuildContainer);
dockerLogsContainer.targetContainerId(createBuildContainer.getContainerId());
dockerLogsContainer.getFollow().set(true);
});
final DockerWaitContainer waitForBuildContainer = project.getTasks().create("waitForBuildContainer", DockerWaitContainer.class,
dockerWaitContainer -> {
dockerWaitContainer.dependsOn(logBuildContainer);
dockerWaitContainer.targetContainerId(createBuildContainer.getContainerId());
dockerWaitContainer.doLast(task -> {
if (dockerWaitContainer.getExitCode() != 0) {
throw new GradleException("Failed to build docker image, aborting build");
}
});
});
final DockerCommitImage commitBuildImageResult = project.getTasks().create("commitBuildImageResult", DockerCommitImage.class,
dockerCommitImage -> {
dockerCommitImage.dependsOn(waitForBuildContainer);
dockerCommitImage.targetContainerId(createBuildContainer.getContainerId());
});
final DockerTagImage tagBuildImageResult = project.getTasks().create("tagBuildImageResult", DockerTagImage.class, dockerTagImage -> {
dockerTagImage.dependsOn(commitBuildImageResult);
dockerTagImage.getImageId().set(commitBuildImageResult.getImageId());
dockerTagImage.getTag().set(System.getProperty("docker.provided.tag",
"${UUID.randomUUID().toString().toLowerCase().subSequence(0, 12)}"));
dockerTagImage.getRepository().set(registryName);
});
final DockerPushImage pushBuildImage = project.getTasks().create("pushBuildImage", DockerPushImage.class, dockerPushImage -> {
dockerPushImage.dependsOn(tagBuildImageResult);
dockerPushImage.doFirst(task -> dockerPushImage.setRegistryCredentials(registryCredentialsForPush));
dockerPushImage.getImageName().set(registryName);
dockerPushImage.getTag().set(tagBuildImageResult.getTag());
});
this.pushTask = pushBuildImage;
final DockerRemoveContainer deleteContainer = project.getTasks().create("deleteBuildContainer", DockerRemoveContainer.class,
dockerRemoveContainer -> {
dockerRemoveContainer.dependsOn(pushBuildImage);
dockerRemoveContainer.targetContainerId(createBuildContainer.getContainerId());
});
final DockerRemoveImage deleteTaggedImage = project.getTasks().create("deleteTaggedImage", DockerRemoveImage.class,
dockerRemoveImage -> {
dockerRemoveImage.dependsOn(pushBuildImage);
dockerRemoveImage.getForce().set(true);
dockerRemoveImage.targetImageId(commitBuildImageResult.getImageId());
});
final DockerRemoveImage deleteBuildImage = project.getTasks().create("deleteBuildImage", DockerRemoveImage.class,
dockerRemoveImage -> {
dockerRemoveImage.dependsOn(deleteContainer, deleteTaggedImage);
dockerRemoveImage.getForce().set(true);
dockerRemoveImage.targetImageId(buildDockerImageForSource.getImageId());
});
if (System.getProperty("docker.keep.image") == null) {
pushBuildImage.finalizedBy(deleteContainer, deleteBuildImage, deleteTaggedImage);
}
}
}

View File

@ -1,325 +0,0 @@
package net.corda.testing
import io.fabric8.kubernetes.api.model.*
import io.fabric8.kubernetes.client.*
import io.fabric8.kubernetes.client.dsl.ExecListener
import io.fabric8.kubernetes.client.dsl.ExecWatch
import io.fabric8.kubernetes.client.utils.Serialization
import okhttp3.Response
import org.gradle.api.DefaultTask
import org.gradle.api.GradleException
import org.gradle.api.tasks.TaskAction
import java.nio.file.Files
import java.nio.file.Path
import java.util.concurrent.CompletableFuture
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
import java.util.stream.Collectors
import java.util.stream.IntStream
class KubesTest extends DefaultTask {
static final ExecutorService executorService = Executors.newCachedThreadPool()
static final ExecutorService singleThreadedExecutor = Executors.newSingleThreadExecutor()
String dockerTag
String fullTaskToExecutePath
String taskToExecuteName
Boolean printOutput = false
Integer numberOfCoresPerFork = 4
Integer memoryGbPerFork = 6
public volatile List<File> testOutput = Collections.emptyList()
public volatile List<KubePodResult> containerResults = Collections.emptyList()
String namespace = "thisisatest"
int k8sTimeout = 50 * 1_000
int webSocketTimeout = k8sTimeout * 6
int numberOfPods = 20
int timeoutInMinutesForPodToStart = 60
Distribution distribution = Distribution.METHOD
@TaskAction
void runTestsOnKubes() {
try {
Class.forName("org.apache.commons.compress.archivers.tar.TarArchiveInputStream")
} catch (ClassNotFoundException ignored) {
throw new GradleException("Apache Commons compress has not be loaded, this can happen if running from within intellj - please select \"delegate to gradle\" for build and test actions")
}
def buildId = System.getProperty("buildId") ? System.getProperty("buildId") :
(project.hasProperty("corda_revision") ? project.property("corda_revision").toString() : "0")
def currentUser = System.getProperty("user.name") ? System.getProperty("user.name") : "UNKNOWN_USER"
String stableRunId = new BigInteger(64, new Random(buildId.hashCode() + currentUser.hashCode() + taskToExecuteName.hashCode())).toString(36).toLowerCase()
String suffix = new BigInteger(64, new Random()).toString(36).toLowerCase()
io.fabric8.kubernetes.client.Config config = new io.fabric8.kubernetes.client.ConfigBuilder()
.withConnectionTimeout(k8sTimeout)
.withRequestTimeout(k8sTimeout)
.withRollingTimeout(k8sTimeout)
.withWebsocketTimeout(webSocketTimeout)
.withWebsocketPingInterval(webSocketTimeout)
.build()
final KubernetesClient client = new DefaultKubernetesClient(config)
try {
client.pods().inNamespace(namespace).list().getItems().forEach({ podToDelete ->
if (podToDelete.getMetadata().name.contains(stableRunId)) {
project.logger.lifecycle("deleting: " + podToDelete.getMetadata().getName())
client.resource(podToDelete).delete()
}
})
} catch (Exception ignored) {
//it's possible that a pod is being deleted by the original build, this can lead to racey conditions
}
List<CompletableFuture<KubePodResult>> futures = IntStream.range(0, numberOfPods).mapToObj({ i ->
String potentialPodName = (taskToExecuteName + "-" + stableRunId + suffix + i).toLowerCase()
String podName = potentialPodName.substring(0, Math.min(potentialPodName.size(), 62))
runBuild(client, namespace, numberOfPods, i, podName, printOutput, 3)
}).collect(Collectors.toList())
this.testOutput = Collections.synchronizedList(futures.collect { it -> it.get().binaryResults }.flatten())
this.containerResults = futures.collect { it -> it.get() }
}
CompletableFuture<KubePodResult> runBuild(KubernetesClient client,
String namespace,
int numberOfPods,
int podIdx,
String podName,
boolean printOutput,
int numberOfRetries) {
CompletableFuture<KubePodResult> toReturn = new CompletableFuture<KubePodResult>()
executorService.submit({
int tryCount = 0
Pod createdPod = null
while (tryCount < numberOfRetries) {
try {
Pod podRequest = buildPod(podName)
project.logger.lifecycle("requesting pod: " + podName)
createdPod = client.pods().inNamespace(namespace).create(podRequest)
project.logger.lifecycle("scheduled pod: " + podName)
File outputFile = Files.createTempFile("container", ".log").toFile()
attachStatusListenerToPod(client, namespace, podName)
schedulePodForDeleteOnShutdown(podName, client, createdPod)
waitForPodToStart(podName, client, namespace)
def stdOutOs = new PipedOutputStream()
def stdOutIs = new PipedInputStream(4096)
ByteArrayOutputStream errChannelStream = new ByteArrayOutputStream();
KubePodResult result = new KubePodResult(createdPod, null, outputFile)
CompletableFuture<KubePodResult> waiter = new CompletableFuture<>()
ExecListener execListener = buildExecListenerForPod(podName, errChannelStream, waiter, result)
stdOutIs.connect(stdOutOs)
String[] buildCommand = getBuildCommand(numberOfPods, podIdx)
ExecWatch execWatch = client.pods().inNamespace(namespace).withName(podName)
.writingOutput(stdOutOs)
.writingErrorChannel(errChannelStream)
.usingListener(execListener).exec(buildCommand)
startLogPumping(outputFile, stdOutIs, podIdx, printOutput)
KubePodResult execResult = waiter.join()
project.logger.lifecycle("build has ended on on pod ${podName} (${podIdx}/${numberOfPods})")
project.logger.lifecycle "Gathering test results from ${execResult.createdPod.metadata.name}"
def binaryResults = downloadTestXmlFromPod(client, namespace, execResult.createdPod)
project.logger.lifecycle("deleting: " + execResult.createdPod.getMetadata().getName())
client.resource(execResult.createdPod).delete()
result.binaryResults = binaryResults
toReturn.complete(result)
break
} catch (Exception e) {
logger.error("Encountered error during testing cycle on pod ${podName} (${podIdx}/${numberOfPods})", e)
try {
if (createdPod) {
client.pods().delete(createdPod)
while (client.pods().inNamespace(namespace).list().getItems().find { p -> p.metadata.name == podName }) {
logger.warn("pod ${podName} has not been deleted, waiting 1s")
Thread.sleep(1000)
}
}
} catch (Exception ignored) {
}
tryCount++
logger.lifecycle("will retry ${podName} another ${numberOfRetries - tryCount} times")
}
}
if (tryCount >= numberOfRetries) {
toReturn.completeExceptionally(new RuntimeException("Failed to build in pod ${podName} (${podIdx}/${numberOfPods}) within retry limit"))
}
})
return toReturn
}
void startLogPumping(File outputFile, stdOutIs, podIdx, boolean printOutput) {
Thread loggingThread = new Thread({ ->
BufferedWriter out = null
BufferedReader br = null
try {
out = new BufferedWriter(new FileWriter(outputFile))
br = new BufferedReader(new InputStreamReader(stdOutIs))
String line
while ((line = br.readLine()) != null) {
def toWrite = ("${taskToExecuteName}/Container" + podIdx + ": " + line).trim()
if (printOutput) {
project.logger.lifecycle(toWrite)
}
out.println(toWrite)
}
} catch (IOException ignored) {
}
finally {
out?.close()
br?.close()
}
})
loggingThread.setDaemon(true)
loggingThread.start()
}
ExecListener buildExecListenerForPod(podName, errChannelStream, CompletableFuture<KubePodResult> waitingFuture, KubePodResult result) {
new ExecListener() {
final Long start = System.currentTimeMillis()
@Override
void onOpen(Response response) {
project.logger.lifecycle("Build started on pod $podName")
}
@Override
void onFailure(Throwable t, Response response) {
project.logger.lifecycle("Received error from rom pod $podName")
waitingFuture.completeExceptionally(t)
}
@Override
void onClose(int code, String reason) {
project.logger.lifecycle("Received onClose() from pod ${podName}, build took: ${(System.currentTimeMillis() - start) / 1000} seconds")
try {
def errChannelContents = errChannelStream.toString()
Status status = Serialization.unmarshal(errChannelContents, Status.class);
result.resultCode = status.details?.causes?.first()?.message?.toInteger() ? status.details?.causes?.first()?.message?.toInteger() : 0
waitingFuture.complete(result)
} catch (Exception e) {
waitingFuture.completeExceptionally(e)
}
}
}
}
void schedulePodForDeleteOnShutdown(String podName, client, Pod createdPod) {
project.logger.info("attaching shutdown hook for pod ${podName}")
Runtime.getRuntime().addShutdownHook({
println "Deleting pod: " + podName
client.pods().delete(createdPod)
})
}
Watch attachStatusListenerToPod(KubernetesClient client, String namespace, String podName) {
client.pods().inNamespace(namespace).withName(podName).watch(new Watcher<Pod>() {
@Override
void eventReceived(Watcher.Action action, Pod resource) {
project.logger.lifecycle("[StatusChange] pod ${resource.getMetadata().getName()} ${action.name()} (${resource.status.phase})")
}
@Override
void onClose(KubernetesClientException cause) {
}
})
}
void waitForPodToStart(String podName, KubernetesClient client, String namespace) {
project.logger.lifecycle("Waiting for pod " + podName + " to start before executing build")
client.pods().inNamespace(namespace).withName(podName).waitUntilReady(timeoutInMinutesForPodToStart, TimeUnit.MINUTES)
project.logger.lifecycle("pod " + podName + " has started, executing build")
}
Pod buildPod(String podName) {
return new PodBuilder().withNewMetadata().withName(podName).endMetadata()
.withNewSpec()
.addNewVolume()
.withName("gradlecache")
.withNewHostPath()
.withPath("/tmp/gradle")
.withType("DirectoryOrCreate")
.endHostPath()
.endVolume()
.addNewContainer()
.withImage(dockerTag)
.withCommand("bash")
.withArgs("-c", "sleep 3600")
.addNewEnv()
.withName("DRIVER_NODE_MEMORY")
.withValue("1024m")
.withName("DRIVER_WEB_MEMORY")
.withValue("1024m")
.endEnv()
.withName(podName)
.withNewResources()
.addToRequests("cpu", new Quantity("${numberOfCoresPerFork}"))
.addToRequests("memory", new Quantity("${memoryGbPerFork}Gi"))
.endResources()
.addNewVolumeMount()
.withName("gradlecache")
.withMountPath("/tmp/gradle")
.endVolumeMount()
.endContainer()
.withImagePullSecrets(new LocalObjectReference("regcred"))
.withRestartPolicy("Never")
.endSpec()
.build()
}
String[] getBuildCommand(int numberOfPods, int podIdx) {
return ["bash", "-c",
"let x=1 ; while [ \${x} -ne 0 ] ; do echo \"Waiting for DNS\" ; curl services.gradle.org > /dev/null 2>&1 ; x=\$? ; sleep 1 ; done ; " +
"cd /tmp/source ; " +
"let y=1 ; while [ \${y} -ne 0 ] ; do echo \"Preparing build directory\" ; ./gradlew testClasses integrationTestClasses --parallel 2>&1 ; y=\$? ; sleep 1 ; done ;" +
"./gradlew -D${ListTests.DISTRIBUTION_PROPERTY}=${distribution.name()} -Dkubenetize -PdockerFork=" + podIdx + " -PdockerForks=" + numberOfPods + " $fullTaskToExecutePath --info 2>&1 ;" +
"let rs=\$? ; sleep 10 ; exit \${rs}"]
}
Collection<File> downloadTestXmlFromPod(KubernetesClient client, String namespace, Pod cp) {
String resultsInContainerPath = "/tmp/source/build/test-reports"
String binaryResultsFile = "results.bin"
String podName = cp.getMetadata().getName()
Path tempDir = new File(new File(project.getBuildDir(), "test-results-xml"), podName).toPath()
if (!tempDir.toFile().exists()) {
tempDir.toFile().mkdirs()
}
project.logger.lifecycle("Saving " + podName + " results to: " + tempDir.toAbsolutePath().toFile().getAbsolutePath())
client.pods()
.inNamespace(namespace)
.withName(podName)
.dir(resultsInContainerPath)
.copy(tempDir)
return findFolderContainingBinaryResultsFile(new File(tempDir.toFile().getAbsolutePath()), binaryResultsFile)
}
List<File> findFolderContainingBinaryResultsFile(File start, String fileNameToFind) {
Queue<File> filesToInspect = new LinkedList<>(Collections.singletonList(start))
List<File> folders = new ArrayList<>()
while (!filesToInspect.isEmpty()) {
File fileToInspect = filesToInspect.poll()
if (fileToInspect.getAbsolutePath().endsWith(fileNameToFind)) {
folders.add(fileToInspect.parentFile)
}
if (fileToInspect.isDirectory()) {
filesToInspect.addAll(Arrays.stream(fileToInspect.listFiles()).collect(Collectors.toList()))
}
}
return folders
}
}

View File

@ -0,0 +1,388 @@
package net.corda.testing;
import io.fabric8.kubernetes.api.model.*;
import io.fabric8.kubernetes.client.*;
import io.fabric8.kubernetes.client.dsl.ExecListener;
import io.fabric8.kubernetes.client.dsl.PodResource;
import io.fabric8.kubernetes.client.utils.Serialization;
import net.corda.testing.retry.Retry;
import okhttp3.Response;
import org.gradle.api.DefaultTask;
import org.gradle.api.tasks.TaskAction;
import org.jetbrains.annotations.NotNull;
import java.io.*;
import java.math.BigInteger;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class KubesTest extends DefaultTask {
static final String TEST_RUN_DIR = "/test-runs";
private static final ExecutorService executorService = Executors.newCachedThreadPool();
/**
* Name of the k8s Secret object that holds the credentials to access the docker image registry
*/
private static final String REGISTRY_CREDENTIALS_SECRET_NAME = "regcred";
String dockerTag;
String fullTaskToExecutePath;
String taskToExecuteName;
Boolean printOutput = false;
Integer numberOfCoresPerFork = 4;
Integer memoryGbPerFork = 6;
public volatile List<File> testOutput = Collections.emptyList();
public volatile List<KubePodResult> containerResults = Collections.emptyList();
String namespace = "thisisatest";
int k8sTimeout = 50 * 1_000;
int webSocketTimeout = k8sTimeout * 6;
int numberOfPods = 20;
int timeoutInMinutesForPodToStart = 60;
Distribution distribution = Distribution.METHOD;
@TaskAction
public void runDistributedTests() {
String buildId = System.getProperty("buildId", "0");
String currentUser = System.getProperty("user.name", "UNKNOWN_USER");
String stableRunId = rnd64Base36(new Random(buildId.hashCode() + currentUser.hashCode() + taskToExecuteName.hashCode()));
String random = rnd64Base36(new Random());
final KubernetesClient client = getKubernetesClient();
try {
client.pods().inNamespace(namespace).list().getItems().forEach(podToDelete -> {
if (podToDelete.getMetadata().getName().contains(stableRunId)) {
getProject().getLogger().lifecycle("deleting: " + podToDelete.getMetadata().getName());
client.resource(podToDelete).delete();
}
});
} catch (Exception ignored) {
//it's possible that a pod is being deleted by the original build, this can lead to racey conditions
}
List<Future<KubePodResult>> futures = IntStream.range(0, numberOfPods).mapToObj(i -> {
String podName = taskToExecuteName.toLowerCase() + "-" + stableRunId + "-" + random + "-" + i;
return submitBuild(client, namespace, numberOfPods, i, podName, printOutput, 3);
}).collect(Collectors.toList());
this.testOutput = Collections.synchronizedList(futures.stream().map(it -> {
try {
return it.get().getBinaryResults();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
}).flatMap(Collection::stream).collect(Collectors.toList()));
this.containerResults = futures.stream().map(it -> {
try {
return it.get();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
}).collect(Collectors.toList());
}
@NotNull
private KubernetesClient getKubernetesClient() {
io.fabric8.kubernetes.client.Config config = new io.fabric8.kubernetes.client.ConfigBuilder()
.withConnectionTimeout(k8sTimeout)
.withRequestTimeout(k8sTimeout)
.withRollingTimeout(k8sTimeout)
.withWebsocketTimeout(webSocketTimeout)
.withWebsocketPingInterval(webSocketTimeout)
.build();
return new DefaultKubernetesClient(config);
}
private static String rnd64Base36(Random rnd) {
return new BigInteger(64, rnd)
.toString(36)
.toLowerCase();
}
private Future<KubePodResult> submitBuild(
KubernetesClient client,
String namespace,
int numberOfPods,
int podIdx,
String podName,
boolean printOutput,
int numberOfRetries
) {
return executorService.submit(() -> buildRunPodWithRetriesOrThrow(client, namespace, numberOfPods, podIdx, podName, printOutput, numberOfRetries));
}
private static void addShutdownHook(Runnable hook) {
Runtime.getRuntime().addShutdownHook(new Thread(hook));
}
private PersistentVolumeClaim createPvc(KubernetesClient client, String name) {
PersistentVolumeClaim pvc = client.persistentVolumeClaims()
.inNamespace(namespace)
.createNew()
.editOrNewMetadata().withName(name).endMetadata()
.editOrNewSpec()
.withAccessModes("ReadWriteOnce")
.editOrNewResources().addToRequests("storage", new Quantity("100Mi")).endResources()
.endSpec()
.done();
addShutdownHook(() -> {
System.out.println("Deleing PVC: " + pvc.getMetadata().getName());
client.persistentVolumeClaims().delete(pvc);
});
return pvc;
}
private KubePodResult buildRunPodWithRetriesOrThrow(
KubernetesClient client,
String namespace,
int numberOfPods,
int podIdx,
String podName,
boolean printOutput,
int numberOfRetries
) {
addShutdownHook(() -> {
System.out.println("deleting pod: " + podName);
client.pods().inNamespace(namespace).withName(podName).delete();
});
try {
// pods might die, so we retry
return Retry.fixed(numberOfRetries).run(() -> {
// remove pod if exists
PodResource<Pod, DoneablePod> oldPod = client.pods().inNamespace(namespace).withName(podName);
if (oldPod.get() != null) {
getLogger().lifecycle("deleting pod: {}", podName);
oldPod.delete();
while (oldPod.get() != null) {
getLogger().info("waiting for pod {} to be removed", podName);
Thread.sleep(1000);
}
}
// recreate and run
getProject().getLogger().lifecycle("creating pod: " + podName);
Pod createdPod = client.pods().inNamespace(namespace).create(buildPodRequest(podName));
getProject().getLogger().lifecycle("scheduled pod: " + podName);
attachStatusListenerToPod(client, createdPod);
waitForPodToStart(client, createdPod);
PipedOutputStream stdOutOs = new PipedOutputStream();
PipedInputStream stdOutIs = new PipedInputStream(4096);
ByteArrayOutputStream errChannelStream = new ByteArrayOutputStream();
CompletableFuture<Integer> waiter = new CompletableFuture<>();
ExecListener execListener = buildExecListenerForPod(podName, errChannelStream, waiter);
stdOutIs.connect(stdOutOs);
client.pods().inNamespace(namespace).withName(podName)
.writingOutput(stdOutOs)
.writingErrorChannel(errChannelStream)
.usingListener(execListener)
.exec(getBuildCommand(numberOfPods, podIdx));
File podOutput = startLogPumping(stdOutIs, podIdx, printOutput);
int resCode = waiter.join();
getProject().getLogger().lifecycle("build has ended on on pod " + podName + " (" + podIdx + "/" + numberOfPods + "), gathering results");
Collection<File> binaryResults = downloadTestXmlFromPod(client, namespace, createdPod);
return new KubePodResult(resCode, podOutput, binaryResults);
});
} catch (Retry.RetryException e) {
throw new RuntimeException("Failed to build in pod " + podName + " (" + podIdx + "/" + numberOfPods + ") in " + numberOfRetries + " attempts", e);
}
}
private Pod buildPodRequest(String podName) {
return new PodBuilder()
.withNewMetadata().withName(podName).endMetadata()
.withNewSpec()
.addNewVolume()
.withName("gradlecache")
.withNewHostPath()
.withType("DirectoryOrCreate")
.withPath("/tmp/gradle")
.endHostPath()
.endVolume()
.addNewVolume()
.withName("testruns")
.withNewHostPath()
.withType("DirectoryOrCreate")
.withPath("/tmp/testruns")
.endHostPath()
.endVolume()
// .addNewVolume()
// .withName("testruns")
// .withNewPersistentVolumeClaim()
// .withClaimName(pvc.getMetadata().getName())
// .endPersistentVolumeClaim()
// .endVolume()
.addNewContainer()
.withImage(dockerTag)
.withCommand("bash")
.withArgs("-c", "sleep 3600")
.addNewEnv()
.withName("DRIVER_NODE_MEMORY")
.withValue("1024m")
.withName("DRIVER_WEB_MEMORY")
.withValue("1024m")
.endEnv()
.withName(podName)
.withNewResources()
.addToRequests("cpu", new Quantity(numberOfCoresPerFork.toString()))
.addToRequests("memory", new Quantity(memoryGbPerFork.toString() + "Gi"))
.endResources()
.addNewVolumeMount().withName("gradlecache").withMountPath("/tmp/gradle").endVolumeMount()
.addNewVolumeMount().withName("testruns").withMountPath(TEST_RUN_DIR).endVolumeMount()
.endContainer()
.addNewImagePullSecret(REGISTRY_CREDENTIALS_SECRET_NAME)
.withRestartPolicy("Never")
.endSpec()
.build();
}
private File startLogPumping(InputStream stdOutIs, int podIdx, boolean printOutput) throws IOException {
File outputFile = Files.createTempFile("container", ".log").toFile();
Thread loggingThread = new Thread(() -> {
try (BufferedWriter out = new BufferedWriter(new FileWriter(outputFile));
BufferedReader br = new BufferedReader(new InputStreamReader(stdOutIs))) {
String line;
while ((line = br.readLine()) != null) {
String toWrite = ("Container" + podIdx + ": " + line).trim();
if (printOutput) {
getProject().getLogger().lifecycle(toWrite);
}
out.write(line);
out.newLine();
}
} catch (IOException ignored) {
}
});
loggingThread.setDaemon(true);
loggingThread.start();
return outputFile;
}
private Watch attachStatusListenerToPod(KubernetesClient client, Pod pod) {
return client.pods().inNamespace(pod.getMetadata().getNamespace()).withName(pod.getMetadata().getName()).watch(new Watcher<Pod>() {
@Override
public void eventReceived(Watcher.Action action, Pod resource) {
getProject().getLogger().lifecycle("[StatusChange] pod " + resource.getMetadata().getName() + " " + action.name() + " (" + resource.getStatus().getPhase() + ")");
}
@Override
public void onClose(KubernetesClientException cause) {
}
});
}
private void waitForPodToStart(KubernetesClient client, Pod pod) {
getProject().getLogger().lifecycle("Waiting for pod " + pod.getMetadata().getName() + " to start before executing build");
try {
client.pods().inNamespace(pod.getMetadata().getNamespace()).withName(pod.getMetadata().getName()).waitUntilReady(timeoutInMinutesForPodToStart, TimeUnit.MINUTES);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
getProject().getLogger().lifecycle("pod " + pod.getMetadata().getName() + " has started, executing build");
}
private Collection<File> downloadTestXmlFromPod(KubernetesClient client, String namespace, Pod cp) {
String resultsInContainerPath = "/tmp/source/build/test-reports";
String binaryResultsFile = "results.bin";
String podName = cp.getMetadata().getName();
Path tempDir = new File(new File(getProject().getBuildDir(), "test-results-xml"), podName).toPath();
if (!tempDir.toFile().exists()) {
tempDir.toFile().mkdirs();
}
getProject().getLogger().lifecycle("Saving " + podName + " results to: " + tempDir.toAbsolutePath().toFile().getAbsolutePath());
client.pods()
.inNamespace(namespace)
.withName(podName)
.dir(resultsInContainerPath)
.copy(tempDir);
return findFolderContainingBinaryResultsFile(new File(tempDir.toFile().getAbsolutePath()), binaryResultsFile);
}
private String[] getBuildCommand(int numberOfPods, int podIdx) {
String shellScript = "let x=1 ; while [ ${x} -ne 0 ] ; do echo \"Waiting for DNS\" ; curl services.gradle.org > /dev/null 2>&1 ; x=$? ; sleep 1 ; done ; " + "cd /tmp/source ; " +
"let y=1 ; while [ ${y} -ne 0 ] ; do echo \"Preparing build directory\" ; ./gradlew testClasses integrationTestClasses --parallel 2>&1 ; y=$? ; sleep 1 ; done ;" +
"./gradlew -D" + ListTests.DISTRIBUTION_PROPERTY + "=" + distribution.name() + " -Dkubenetize -PdockerFork=" + podIdx + " -PdockerForks=" + numberOfPods + " " + fullTaskToExecutePath + " --info 2>&1 ;" +
"let rs=$? ; sleep 10 ; exit ${rs}";
return new String[]{"bash", "-c", shellScript};
}
private List<File> findFolderContainingBinaryResultsFile(File start, String fileNameToFind) {
Queue<File> filesToInspect = new LinkedList<>(Collections.singletonList(start));
List<File> folders = new ArrayList<>();
while (!filesToInspect.isEmpty()) {
File fileToInspect = filesToInspect.poll();
if (fileToInspect.getAbsolutePath().endsWith(fileNameToFind)) {
folders.add(fileToInspect.getParentFile());
}
if (fileToInspect.isDirectory()) {
filesToInspect.addAll(Arrays.stream(fileToInspect.listFiles()).collect(Collectors.toList()));
}
}
return folders;
}
private ExecListener buildExecListenerForPod(String podName, ByteArrayOutputStream errChannelStream, CompletableFuture<Integer> waitingFuture) {
return new ExecListener() {
final Long start = System.currentTimeMillis();
@Override
public void onOpen(Response response) {
getProject().getLogger().lifecycle("Build started on pod " + podName);
}
@Override
public void onFailure(Throwable t, Response response) {
getProject().getLogger().lifecycle("Received error from rom pod " + podName);
waitingFuture.completeExceptionally(t);
}
@Override
public void onClose(int code, String reason) {
getProject().getLogger().lifecycle("Received onClose() from pod " + podName + " , build took: " + ((System.currentTimeMillis() - start) / 1000) + " seconds");
try {
String errChannelContents = errChannelStream.toString();
Status status = Serialization.unmarshal(errChannelContents, Status.class);
Integer resultCode = Optional.ofNullable(status).map(Status::getDetails)
.map(StatusDetails::getCauses)
.flatMap(c -> c.stream().findFirst())
.map(StatusCause::getMessage)
.map(Integer::parseInt).orElse(0);
waitingFuture.complete(resultCode);
} catch (Exception e) {
waitingFuture.completeExceptionally(e);
}
}
};
}
}

View File

@ -79,7 +79,7 @@ class ListTests extends DefaultTask implements TestLister {
.collect { c -> (c.getSubclasses() + Collections.singletonList(c)) } .collect { c -> (c.getSubclasses() + Collections.singletonList(c)) }
.flatten() .flatten()
.collect { ClassInfo c -> .collect { ClassInfo c ->
c.getMethodInfo().filter { m -> m.hasAnnotation("org.junit.Test") }.collect { m -> c.name + "." + m.name + "*" } c.getMethodInfo().filter { m -> m.hasAnnotation("org.junit.Test") }.collect { m -> c.name + "." + m.name }
}.flatten() }.flatten()
.toSet() .toSet()
@ -97,7 +97,7 @@ class ListTests extends DefaultTask implements TestLister {
.getClassesWithMethodAnnotation("org.junit.Test") .getClassesWithMethodAnnotation("org.junit.Test")
.collect { c -> (c.getSubclasses() + Collections.singletonList(c)) } .collect { c -> (c.getSubclasses() + Collections.singletonList(c)) }
.flatten() .flatten()
.collect { ClassInfo c -> c.name + "*" }.flatten() .collect { ClassInfo c -> c.name }.flatten()
.toSet() .toSet()
this.allTests = results.stream().sorted().collect(Collectors.toList()) this.allTests = results.stream().sorted().collect(Collectors.toList())
break break

View File

@ -1,41 +1,29 @@
package net.corda.testing; package net.corda.testing;
import io.fabric8.kubernetes.api.model.Pod;
import java.io.File; import java.io.File;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.CompletableFuture;
public class KubePodResult { public class KubePodResult {
private final Pod createdPod; private final int resultCode;
private final CompletableFuture<Void> waiter;
private volatile Integer resultCode = 255;
private final File output; private final File output;
private volatile Collection<File> binaryResults = Collections.emptyList(); private final Collection<File> binaryResults;
KubePodResult(Pod createdPod, CompletableFuture<Void> waiter, File output) { public KubePodResult(int resultCode, File output, Collection<File> binaryResults) {
this.createdPod = createdPod; this.resultCode = resultCode;
this.waiter = waiter;
this.output = output; this.output = output;
this.binaryResults = binaryResults;
} }
public void setResultCode(Integer code) { public int getResultCode() {
synchronized (createdPod) { return resultCode;
this.resultCode = code;
}
}
public Integer getResultCode() {
synchronized (createdPod) {
return this.resultCode;
}
} }
public File getOutput() { public File getOutput() {
synchronized (createdPod) { return output;
return output;
}
} }
};
public Collection<File> getBinaryResults() {
return binaryResults;
}
}

View File

@ -152,10 +152,10 @@ public class KubesReporting extends DefaultTask {
if (!containersWithNonZeroReturnCodes.isEmpty()) { if (!containersWithNonZeroReturnCodes.isEmpty()) {
String reportUrl = new ConsoleRenderer().asClickableFileUrl(new File(destinationDir, "index.html")); String reportUrl = new ConsoleRenderer().asClickableFileUrl(new File(destinationDir, "index.html"));
if (shouldPrintOutput){ if (shouldPrintOutput){
containersWithNonZeroReturnCodes.forEach(container -> { containersWithNonZeroReturnCodes.forEach(podResult -> {
try { try {
System.out.println("\n##### CONTAINER OUTPUT START #####"); System.out.println("\n##### CONTAINER OUTPUT START #####");
IOUtils.copy(new FileInputStream(container.getOutput()), System.out); IOUtils.copy(new FileInputStream(podResult.getOutput()), System.out);
System.out.println("##### CONTAINER OUTPUT END #####\n"); System.out.println("##### CONTAINER OUTPUT END #####\n");
} catch (IOException ignored) { } catch (IOException ignored) {
} }

View File

@ -0,0 +1,48 @@
package net.corda.testing.retry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.Callable;
public final class Retry {
private static final Logger log = LoggerFactory.getLogger(Retry.class);
public interface RetryStrategy {
<T> T run(Callable<T> op) throws RetryException;
}
public static final class RetryException extends RuntimeException {
public RetryException(String message) {
super(message);
}
public RetryException(String message, Throwable cause) {
super(message, cause);
}
}
public static RetryStrategy fixed(int times) {
if (times < 1) throw new IllegalArgumentException();
return new RetryStrategy() {
@Override
public <T> T run(Callable<T> op) {
int run = 0;
Exception last = null;
while (run < times) {
try {
return op.call();
} catch (Exception e) {
last = e;
log.info("Exception caught: " + e.getMessage());
}
run++;
}
throw new RetryException("Operation failed " + run + " times", last);
}
};
}
}

View File

@ -21,12 +21,12 @@ public class BucketingAllocatorTest {
BucketingAllocator bucketingAllocator = new BucketingAllocator(1, Collections::emptyList); BucketingAllocator bucketingAllocator = new BucketingAllocator(1, Collections::emptyList);
Object task = new Object(); Object task = new Object();
bucketingAllocator.addSource(() -> Arrays.asList("SomeTestingClass*", "AnotherTestingClass*"), task); bucketingAllocator.addSource(() -> Arrays.asList("SomeTestingClass", "AnotherTestingClass"), task);
bucketingAllocator.generateTestPlan(); bucketingAllocator.generateTestPlan();
List<String> testsForForkAndTestTask = bucketingAllocator.getTestsForForkAndTestTask(0, task); List<String> testsForForkAndTestTask = bucketingAllocator.getTestsForForkAndTestTask(0, task);
Assert.assertThat(testsForForkAndTestTask, IsIterableContainingInAnyOrder.containsInAnyOrder("SomeTestingClass*", "AnotherTestingClass*")); Assert.assertThat(testsForForkAndTestTask, IsIterableContainingInAnyOrder.containsInAnyOrder("SomeTestingClass", "AnotherTestingClass"));
} }
@ -37,7 +37,7 @@ public class BucketingAllocatorTest {
BucketingAllocator bucketingAllocator = new BucketingAllocator(2, Collections::emptyList); BucketingAllocator bucketingAllocator = new BucketingAllocator(2, Collections::emptyList);
Object task = new Object(); Object task = new Object();
bucketingAllocator.addSource(() -> Arrays.asList("SomeTestingClass*", "AnotherTestingClass*"), task); bucketingAllocator.addSource(() -> Arrays.asList("SomeTestingClass", "AnotherTestingClass"), task);
bucketingAllocator.generateTestPlan(); bucketingAllocator.generateTestPlan();
List<String> testsForForkOneAndTestTask = bucketingAllocator.getTestsForForkAndTestTask(0, task); List<String> testsForForkOneAndTestTask = bucketingAllocator.getTestsForForkAndTestTask(0, task);
@ -48,7 +48,7 @@ public class BucketingAllocatorTest {
List<String> allTests = Stream.of(testsForForkOneAndTestTask, testsForForkTwoAndTestTask).flatMap(Collection::stream).collect(Collectors.toList()); List<String> allTests = Stream.of(testsForForkOneAndTestTask, testsForForkTwoAndTestTask).flatMap(Collection::stream).collect(Collectors.toList());
Assert.assertThat(allTests, IsIterableContainingInAnyOrder.containsInAnyOrder("SomeTestingClass*", "AnotherTestingClass*")); Assert.assertThat(allTests, IsIterableContainingInAnyOrder.containsInAnyOrder("SomeTestingClass", "AnotherTestingClass"));
} }
} }

View File

@ -51,6 +51,7 @@ import net.corda.core.utilities.toHexString
import net.corda.serialization.internal.AllWhitelist import net.corda.serialization.internal.AllWhitelist
import net.corda.serialization.internal.amqp.SerializerFactoryBuilder import net.corda.serialization.internal.amqp.SerializerFactoryBuilder
import net.corda.serialization.internal.amqp.hasCordaSerializable import net.corda.serialization.internal.amqp.hasCordaSerializable
import net.corda.serialization.internal.amqp.registerCustomSerializers
import java.math.BigDecimal import java.math.BigDecimal
import java.security.PublicKey import java.security.PublicKey
import java.security.cert.CertPath import java.security.cert.CertPath
@ -116,8 +117,10 @@ private class CordaSerializableClassIntrospector(private val context: Module.Set
} }
private class CordaSerializableBeanSerializerModifier : BeanSerializerModifier() { private class CordaSerializableBeanSerializerModifier : BeanSerializerModifier() {
// We need to pass in a SerializerFactory when scanning for properties, but don't actually do any serialisation so any will do. // We need to pass in a SerializerFactory when scanning for properties, but don't actually do any serialisation so any will do.
private val serializerFactory = SerializerFactoryBuilder.build(AllWhitelist, javaClass.classLoader) private val serializerFactory = SerializerFactoryBuilder.build(AllWhitelist, javaClass.classLoader).also {
registerCustomSerializers(it)
}
override fun changeProperties(config: SerializationConfig, override fun changeProperties(config: SerializationConfig,
beanDesc: BeanDescription, beanDesc: BeanDescription,
@ -125,7 +128,9 @@ private class CordaSerializableBeanSerializerModifier : BeanSerializerModifier()
val beanClass = beanDesc.beanClass val beanClass = beanDesc.beanClass
if (hasCordaSerializable(beanClass) && !SerializeAsToken::class.java.isAssignableFrom(beanClass)) { if (hasCordaSerializable(beanClass) && !SerializeAsToken::class.java.isAssignableFrom(beanClass)) {
val typeInformation = serializerFactory.getTypeInformation(beanClass) val typeInformation = serializerFactory.getTypeInformation(beanClass)
val propertyNames = typeInformation.propertiesOrEmptyMap.mapNotNull { if (it.value.isCalculated) null else it.key } val propertyNames = typeInformation.propertiesOrEmptyMap.mapNotNull {
if (it.value.isCalculated) null else it.key
}
beanProperties.removeIf { it.name !in propertyNames } beanProperties.removeIf { it.name !in propertyNames }
} }
return beanProperties return beanProperties

View File

@ -3,10 +3,15 @@ package net.corda.client.jfx.model
import javafx.beans.property.SimpleObjectProperty import javafx.beans.property.SimpleObjectProperty
import net.corda.client.rpc.CordaRPCClient import net.corda.client.rpc.CordaRPCClient
import net.corda.client.rpc.CordaRPCConnection import net.corda.client.rpc.CordaRPCConnection
import net.corda.client.rpc.GracefulReconnect
import net.corda.core.contracts.ContractState import net.corda.core.contracts.ContractState
import net.corda.core.flows.StateMachineRunId import net.corda.core.flows.StateMachineRunId
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.messaging.* import net.corda.core.messaging.CordaRPCOps
import net.corda.core.messaging.StateMachineInfo
import net.corda.core.messaging.StateMachineTransactionMapping
import net.corda.core.messaging.StateMachineUpdate
import net.corda.core.messaging.vaultTrackBy
import net.corda.core.node.services.NetworkMapCache.MapChange import net.corda.core.node.services.NetworkMapCache.MapChange
import net.corda.core.node.services.Vault import net.corda.core.node.services.Vault
import net.corda.core.node.services.vault.DEFAULT_PAGE_NUM import net.corda.core.node.services.vault.DEFAULT_PAGE_NUM
@ -72,7 +77,7 @@ class NodeMonitorModel : AutoCloseable {
* TODO provide an unsubscribe mechanism * TODO provide an unsubscribe mechanism
*/ */
fun register(nodeHostAndPort: NetworkHostAndPort, username: String, password: String) { fun register(nodeHostAndPort: NetworkHostAndPort, username: String, password: String) {
rpc = CordaRPCClient(nodeHostAndPort).start(username, password) rpc = CordaRPCClient(nodeHostAndPort).start(username, password, GracefulReconnect())
proxyObservable.value = rpc.proxy proxyObservable.value = rpc.proxy
// Vault snapshot (force single page load with MAX_PAGE_SIZE) + updates // Vault snapshot (force single page load with MAX_PAGE_SIZE) + updates

View File

@ -4,6 +4,7 @@ import net.corda.client.rpc.CordaRPCClient
import net.corda.client.rpc.CordaRPCClientConfiguration import net.corda.client.rpc.CordaRPCClientConfiguration
import net.corda.client.rpc.CordaRPCClientTest import net.corda.client.rpc.CordaRPCClientTest
import net.corda.client.rpc.GracefulReconnect import net.corda.client.rpc.GracefulReconnect
import net.corda.client.rpc.MaxRpcRetryException
import net.corda.client.rpc.internal.ReconnectingCordaRPCOps import net.corda.client.rpc.internal.ReconnectingCordaRPCOps
import net.corda.core.messaging.startTrackedFlow import net.corda.core.messaging.startTrackedFlow
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
@ -21,9 +22,11 @@ import net.corda.testing.driver.internal.incrementalPortAllocation
import net.corda.testing.node.User import net.corda.testing.node.User
import net.corda.testing.node.internal.FINANCE_CORDAPPS import net.corda.testing.node.internal.FINANCE_CORDAPPS
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.Test import org.junit.Test
import java.util.concurrent.CountDownLatch import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import kotlin.concurrent.thread
import kotlin.test.assertFalse import kotlin.test.assertFalse
import kotlin.test.assertTrue import kotlin.test.assertTrue
@ -39,7 +42,7 @@ class CordaRPCClientReconnectionTest {
@Test @Test
fun `rpc client calls and returned observables continue working when the server crashes and restarts`() { fun `rpc client calls and returned observables continue working when the server crashes and restarts`() {
driver(DriverParameters(cordappsForAllNodes = FINANCE_CORDAPPS, startNodesInProcess = false, inMemoryDB = false)) { driver(DriverParameters(cordappsForAllNodes = FINANCE_CORDAPPS)) {
val latch = CountDownLatch(2) val latch = CountDownLatch(2)
val address = NetworkHostAndPort("localhost", portAllocator.nextPort()) val address = NetworkHostAndPort("localhost", portAllocator.nextPort())
@ -52,9 +55,7 @@ class CordaRPCClientReconnectionTest {
} }
val node = startNode() val node = startNode()
val client = CordaRPCClient(node.rpcAddress, CordaRPCClientConfiguration.DEFAULT.copy( val client = CordaRPCClient(node.rpcAddress)
maxReconnectAttempts = 5
))
(client.start(rpcUser.username, rpcUser.password, gracefulReconnect = gracefulReconnect)).use { (client.start(rpcUser.username, rpcUser.password, gracefulReconnect = gracefulReconnect)).use {
val rpcOps = it.proxy as ReconnectingCordaRPCOps val rpcOps = it.proxy as ReconnectingCordaRPCOps
@ -79,7 +80,7 @@ class CordaRPCClientReconnectionTest {
@Test @Test
fun `a client can successfully unsubscribe a reconnecting observable`() { fun `a client can successfully unsubscribe a reconnecting observable`() {
driver(DriverParameters(cordappsForAllNodes = FINANCE_CORDAPPS, startNodesInProcess = false, inMemoryDB = false)) { driver(DriverParameters(cordappsForAllNodes = FINANCE_CORDAPPS)) {
val latch = CountDownLatch(2) val latch = CountDownLatch(2)
val address = NetworkHostAndPort("localhost", portAllocator.nextPort()) val address = NetworkHostAndPort("localhost", portAllocator.nextPort())
@ -92,9 +93,7 @@ class CordaRPCClientReconnectionTest {
} }
val node = startNode() val node = startNode()
val client = CordaRPCClient(node.rpcAddress, CordaRPCClientConfiguration.DEFAULT.copy( val client = CordaRPCClient(node.rpcAddress)
maxReconnectAttempts = 5
))
(client.start(rpcUser.username, rpcUser.password, gracefulReconnect = gracefulReconnect)).use { (client.start(rpcUser.username, rpcUser.password, gracefulReconnect = gracefulReconnect)).use {
val rpcOps = it.proxy as ReconnectingCordaRPCOps val rpcOps = it.proxy as ReconnectingCordaRPCOps
@ -119,7 +118,7 @@ class CordaRPCClientReconnectionTest {
@Test @Test
fun `rpc client calls and returned observables continue working when there is failover between servers`() { fun `rpc client calls and returned observables continue working when there is failover between servers`() {
driver(DriverParameters(cordappsForAllNodes = FINANCE_CORDAPPS, startNodesInProcess = false, inMemoryDB = false)) { driver(DriverParameters(cordappsForAllNodes = FINANCE_CORDAPPS)) {
val latch = CountDownLatch(2) val latch = CountDownLatch(2)
fun startNode(address: NetworkHostAndPort): NodeHandle { fun startNode(address: NetworkHostAndPort): NodeHandle {
@ -133,9 +132,7 @@ class CordaRPCClientReconnectionTest {
val addresses = listOf(NetworkHostAndPort("localhost", portAllocator.nextPort()), NetworkHostAndPort("localhost", portAllocator.nextPort())) val addresses = listOf(NetworkHostAndPort("localhost", portAllocator.nextPort()), NetworkHostAndPort("localhost", portAllocator.nextPort()))
val node = startNode(addresses[0]) val node = startNode(addresses[0])
val client = CordaRPCClient(addresses, CordaRPCClientConfiguration.DEFAULT.copy( val client = CordaRPCClient(addresses)
maxReconnectAttempts = 5
))
(client.start(rpcUser.username, rpcUser.password, gracefulReconnect = gracefulReconnect)).use { (client.start(rpcUser.username, rpcUser.password, gracefulReconnect = gracefulReconnect)).use {
val rpcOps = it.proxy as ReconnectingCordaRPCOps val rpcOps = it.proxy as ReconnectingCordaRPCOps
@ -157,4 +154,33 @@ class CordaRPCClientReconnectionTest {
} }
} }
} }
@Test
fun `an RPC call fails, when the maximum number of attempts is exceeded`() {
driver(DriverParameters(cordappsForAllNodes = emptyList())) {
val address = NetworkHostAndPort("localhost", portAllocator.nextPort())
fun startNode(): NodeHandle {
return startNode(
providedName = CHARLIE_NAME,
rpcUsers = listOf(CordaRPCClientTest.rpcUser),
customOverrides = mapOf("rpcSettings.address" to address.toString())
).getOrThrow()
}
val node = startNode()
val client = CordaRPCClient(node.rpcAddress)
(client.start(rpcUser.username, rpcUser.password, gracefulReconnect = GracefulReconnect(maxAttempts = 1))).use {
val rpcOps = it.proxy as ReconnectingCordaRPCOps
node.stop()
thread { startNode() }
assertThatThrownBy { rpcOps.networkParameters }
.isInstanceOf(MaxRpcRetryException::class.java)
}
}
}
} }

View File

@ -282,10 +282,13 @@ open class CordaRPCClientConfiguration @JvmOverloads constructor(
* *
* @param onDisconnect implement this callback to perform logic when the RPC disconnects on connection disconnect * @param onDisconnect implement this callback to perform logic when the RPC disconnects on connection disconnect
* @param onReconnect implement this callback to perform logic when the RPC has reconnected after connection disconnect * @param onReconnect implement this callback to perform logic when the RPC has reconnected after connection disconnect
* @param maxAttempts the maximum number of attempts per each individual RPC call. A negative number indicates infinite number of retries.
* The default value is 5.
*/ */
class GracefulReconnect(val onDisconnect: () -> Unit = {}, val onReconnect: () -> Unit = {}) { class GracefulReconnect(val onDisconnect: () -> Unit = {}, val onReconnect: () -> Unit = {}, val maxAttempts: Int = 5) {
constructor(onDisconnect: Runnable, onReconnect: Runnable ) : @JvmOverloads
this(onDisconnect = { onDisconnect.run() }, onReconnect = { onReconnect.run() }) constructor(onDisconnect: Runnable, onReconnect: Runnable, maxAttempts: Int = 5) :
this(onDisconnect = { onDisconnect.run() }, onReconnect = { onReconnect.run() }, maxAttempts = maxAttempts)
} }
/** /**

View File

@ -9,6 +9,14 @@ open class RPCException(message: String?, cause: Throwable?) : CordaRuntimeExcep
constructor(msg: String) : this(msg, null) constructor(msg: String) : this(msg, null)
} }
/**
* Thrown to indicate an RPC operation has been retried for the [maxNumberOfRetries] unsuccessfully.
* @param maxNumberOfRetries the number of retries that had been performed.
* @param cause the cause of the last failed attempt.
*/
class MaxRpcRetryException(maxNumberOfRetries: Int, cause: Throwable?):
RPCException("Max number of retries ($maxNumberOfRetries) was reached.", cause)
/** /**
* Signals that the underlying [RPCConnection] dropped. * Signals that the underlying [RPCConnection] dropped.
*/ */

View File

@ -55,7 +55,7 @@ class ReconnectingCordaRPCOps private constructor(
username: String, username: String,
password: String, password: String,
rpcConfiguration: CordaRPCClientConfiguration, rpcConfiguration: CordaRPCClientConfiguration,
gracefulReconnect: GracefulReconnect? = null, gracefulReconnect: GracefulReconnect = GracefulReconnect(),
sslConfiguration: ClientRpcSslOptions? = null, sslConfiguration: ClientRpcSslOptions? = null,
classLoader: ClassLoader? = null, classLoader: ClassLoader? = null,
observersPool: ExecutorService observersPool: ExecutorService
@ -117,7 +117,7 @@ class ReconnectingCordaRPCOps private constructor(
val rpcConfiguration: CordaRPCClientConfiguration, val rpcConfiguration: CordaRPCClientConfiguration,
val sslConfiguration: ClientRpcSslOptions? = null, val sslConfiguration: ClientRpcSslOptions? = null,
val classLoader: ClassLoader?, val classLoader: ClassLoader?,
val gracefulReconnect: GracefulReconnect? = null, val gracefulReconnect: GracefulReconnect = GracefulReconnect(),
val observersPool: ExecutorService val observersPool: ExecutorService
) : RPCConnection<CordaRPCOps> { ) : RPCConnection<CordaRPCOps> {
private var currentRPCConnection: CordaRPCConnection? = null private var currentRPCConnection: CordaRPCConnection? = null
@ -147,13 +147,13 @@ class ReconnectingCordaRPCOps private constructor(
// First one to get here gets to do all the reconnect logic, including calling onDisconnect and onReconnect. This makes sure // First one to get here gets to do all the reconnect logic, including calling onDisconnect and onReconnect. This makes sure
// that they're only called once per reconnect. // that they're only called once per reconnect.
currentState = DIED currentState = DIED
gracefulReconnect?.onDisconnect?.invoke() gracefulReconnect.onDisconnect.invoke()
//TODO - handle error cases //TODO - handle error cases
log.error("Reconnecting to ${this.nodeHostAndPorts} due to error: ${e.message}") log.error("Reconnecting to ${this.nodeHostAndPorts} due to error: ${e.message}")
log.debug("", e) log.debug("", e)
connect() connect()
previousConnection?.forceClose() previousConnection?.forceClose()
gracefulReconnect?.onReconnect?.invoke() gracefulReconnect.onReconnect.invoke()
} }
/** /**
* Called on external error. * Called on external error.
@ -249,9 +249,16 @@ class ReconnectingCordaRPCOps private constructor(
} }
} }
private fun doInvoke(method: Method, args: Array<out Any>?): Any? { /**
// will stop looping when [method.invoke] succeeds * This method retries the invoked operation in a loop by re-establishing the connection when there is a problem
while (true) { * and checking if the [maxNumberOfAttempts] has been exhausted.
*
* A negative number for [maxNumberOfAttempts] means an unlimited number of retries will be performed.
*/
private fun doInvoke(method: Method, args: Array<out Any>?, maxNumberOfAttempts: Int): Any? {
var remainingAttempts = maxNumberOfAttempts
var lastException: Throwable? = null
while (remainingAttempts != 0) {
try { try {
log.debug { "Invoking RPC $method..." } log.debug { "Invoking RPC $method..." }
return method.invoke(reconnectingRPCConnection.proxy, *(args ?: emptyArray())).also { return method.invoke(reconnectingRPCConnection.proxy, *(args ?: emptyArray())).also {
@ -280,15 +287,20 @@ class ReconnectingCordaRPCOps private constructor(
checkIfIsStartFlow(method, e) checkIfIsStartFlow(method, e)
} }
} }
lastException = e.targetException
remainingAttempts--
} }
} }
throw MaxRpcRetryException(maxNumberOfAttempts, lastException)
} }
override fun invoke(proxy: Any, method: Method, args: Array<out Any>?): Any? { override fun invoke(proxy: Any, method: Method, args: Array<out Any>?): Any? {
return when (method.returnType) { return when (method.returnType) {
DataFeed::class.java -> { DataFeed::class.java -> {
// Intercept the data feed methods and return a ReconnectingObservable instance // Intercept the data feed methods and return a ReconnectingObservable instance
val initialFeed: DataFeed<Any, Any?> = uncheckedCast(doInvoke(method, args)) val initialFeed: DataFeed<Any, Any?> = uncheckedCast(doInvoke(method, args,
reconnectingRPCConnection.gracefulReconnect.maxAttempts))
val observable = ReconnectingObservable(reconnectingRPCConnection, initialFeed) { val observable = ReconnectingObservable(reconnectingRPCConnection, initialFeed) {
// This handles reconnecting and creates new feeds. // This handles reconnecting and creates new feeds.
uncheckedCast(this.invoke(reconnectingRPCConnection.proxy, method, args)) uncheckedCast(this.invoke(reconnectingRPCConnection.proxy, method, args))
@ -296,7 +308,7 @@ class ReconnectingCordaRPCOps private constructor(
initialFeed.copy(updates = observable) initialFeed.copy(updates = observable)
} }
// TODO - add handlers for Observable return types. // TODO - add handlers for Observable return types.
else -> doInvoke(method, args) else -> doInvoke(method, args, reconnectingRPCConnection.gracefulReconnect.maxAttempts)
} }
} }
} }

View File

@ -6,6 +6,8 @@ import net.corda.core.CordaInternal
import net.corda.core.DeleteForDJVM import net.corda.core.DeleteForDJVM
import net.corda.core.contracts.StateRef import net.corda.core.contracts.StateRef
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.identity.AbstractParty
import net.corda.core.identity.AnonymousParty
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.identity.PartyAndCertificate import net.corda.core.identity.PartyAndCertificate
import net.corda.core.internal.* import net.corda.core.internal.*
@ -120,14 +122,18 @@ abstract class FlowLogic<out T> {
* is routed depends on the [Destination] type, including whether this call does any initial communication. * is routed depends on the [Destination] type, including whether this call does any initial communication.
*/ */
@Suspendable @Suspendable
fun initiateFlow(destination: Destination): FlowSession = stateMachine.initiateFlow(destination) fun initiateFlow(destination: Destination): FlowSession {
require(destination is Party || destination is AnonymousParty) { "Unsupported destination type ${destination.javaClass.name}" }
return stateMachine.initiateFlow(destination, serviceHub.identityService.wellKnownPartyFromAnonymous(destination as AbstractParty)
?: throw IllegalArgumentException("Could not resolve destination: $destination"))
}
/** /**
* Creates a communication session with [party]. Subsequently you may send/receive using this session object. Note * Creates a communication session with [party]. Subsequently you may send/receive using this session object. Note
* that this function does not communicate in itself, the counter-flow will be kicked off by the first send/receive. * that this function does not communicate in itself, the counter-flow will be kicked off by the first send/receive.
*/ */
@Suspendable @Suspendable
fun initiateFlow(party: Party): FlowSession = stateMachine.initiateFlow(party) fun initiateFlow(party: Party): FlowSession = stateMachine.initiateFlow(party, party)
/** /**
* Specifies the identity, with certificate, to use for this flow. This will be one of the multiple identities that * Specifies the identity, with certificate, to use for this flow. This will be one of the multiple identities that

View File

@ -21,6 +21,9 @@ import java.security.SignatureException
* Please note that it will *not* store the transaction to the vault unless that is explicitly requested and checkSufficientSignatures is true. * Please note that it will *not* store the transaction to the vault unless that is explicitly requested and checkSufficientSignatures is true.
* Setting statesToRecord to anything else when checkSufficientSignatures is false will *not* update the vault. * Setting statesToRecord to anything else when checkSufficientSignatures is false will *not* update the vault.
* *
* Attention: At the moment, this flow receives a [SignedTransaction] first thing and then proceeds by invoking a [ResolveTransactionsFlow] subflow.
* This is used as a criterion to identify cases, where a counterparty has failed notarising a transact
*
* @property otherSideSession session to the other side which is calling [SendTransactionFlow]. * @property otherSideSession session to the other side which is calling [SendTransactionFlow].
* @property checkSufficientSignatures if true checks all required signatures are present. See [SignedTransaction.verify]. * @property checkSufficientSignatures if true checks all required signatures are present. See [SignedTransaction.verify].
* @property statesToRecord which transaction states should be recorded in the vault, if any. * @property statesToRecord which transaction states should be recorded in the vault, if any.

View File

@ -18,7 +18,7 @@ interface FlowStateMachine<FLOWRETURN> {
fun <SUSPENDRETURN : Any> suspend(ioRequest: FlowIORequest<SUSPENDRETURN>, maySkipCheckpoint: Boolean): SUSPENDRETURN fun <SUSPENDRETURN : Any> suspend(ioRequest: FlowIORequest<SUSPENDRETURN>, maySkipCheckpoint: Boolean): SUSPENDRETURN
@Suspendable @Suspendable
fun initiateFlow(destination: Destination): FlowSession fun initiateFlow(destination: Destination, wellKnownParty: Party): FlowSession
fun checkFlowPermission(permissionName: String, extraAuditData: Map<String, String>) fun checkFlowPermission(permissionName: String, extraAuditData: Map<String, String>)

View File

@ -85,7 +85,12 @@ val _inheritableContextSerializationEnv = InheritableThreadLocalToggleField<Seri
} }
} }
private val serializationEnvFields = listOf(_nodeSerializationEnv, _driverSerializationEnv, _contextSerializationEnv, _inheritableContextSerializationEnv) private val serializationEnvFields = listOf(
_nodeSerializationEnv,
_driverSerializationEnv,
_contextSerializationEnv,
_inheritableContextSerializationEnv
)
val _allEnabledSerializationEnvs: List<Pair<String, SerializationEnvironment>> val _allEnabledSerializationEnvs: List<Pair<String, SerializationEnvironment>>
get() = serializationEnvFields.mapNotNull { it.get()?.let { env -> Pair(it.name, env) } } get() = serializationEnvFields.mapNotNull { it.get()?.let { env -> Pair(it.name, env) } }
@ -94,7 +99,8 @@ val effectiveSerializationEnv: SerializationEnvironment
get() { get() {
return _allEnabledSerializationEnvs.let { return _allEnabledSerializationEnvs.let {
checkNotNull(it.singleOrNull()?.second) { checkNotNull(it.singleOrNull()?.second) {
"Expected exactly 1 of {${serializationEnvFields.joinToString(", ") { it.name }}} but got: {${it.joinToString(", ") { it.first }}}" "Expected exactly 1 of {${serializationEnvFields.joinToString(", ") { it.name }}} " +
"but got: {${it.joinToString(", ") { it.first }}}"
} }
} }
} }

View File

@ -0,0 +1,6 @@
#Detekt 1.0.1
#Gradle task detektBaseline would always fail due to the fact that the existing baseline would not be taken into
#account so existing issues flare up. We set the max issues allowed to a high number to force detekt to pass when
#generating a baseline.
build:
maxIssues: 9999999

View File

@ -691,8 +691,6 @@
<ID>LongParameterList:Driver.kt$DriverParameters$( isDebug: Boolean, driverDirectory: Path, portAllocation: PortAllocation, debugPortAllocation: PortAllocation, systemProperties: Map&lt;String, String&gt;, useTestClock: Boolean, startNodesInProcess: Boolean, waitForAllNodesToFinish: Boolean, notarySpecs: List&lt;NotarySpec&gt;, extraCordappPackagesToScan: List&lt;String&gt;, jmxPolicy: JmxPolicy, networkParameters: NetworkParameters, cordappsForAllNodes: Set&lt;TestCordapp&gt;? )</ID> <ID>LongParameterList:Driver.kt$DriverParameters$( isDebug: Boolean, driverDirectory: Path, portAllocation: PortAllocation, debugPortAllocation: PortAllocation, systemProperties: Map&lt;String, String&gt;, useTestClock: Boolean, startNodesInProcess: Boolean, waitForAllNodesToFinish: Boolean, notarySpecs: List&lt;NotarySpec&gt;, extraCordappPackagesToScan: List&lt;String&gt;, jmxPolicy: JmxPolicy, networkParameters: NetworkParameters, cordappsForAllNodes: Set&lt;TestCordapp&gt;? )</ID>
<ID>LongParameterList:DriverDSL.kt$DriverDSL$( defaultParameters: NodeParameters = NodeParameters(), providedName: CordaX500Name? = defaultParameters.providedName, rpcUsers: List&lt;User&gt; = defaultParameters.rpcUsers, verifierType: VerifierType = defaultParameters.verifierType, customOverrides: Map&lt;String, Any?&gt; = defaultParameters.customOverrides, startInSameProcess: Boolean? = defaultParameters.startInSameProcess, maximumHeapSize: String = defaultParameters.maximumHeapSize )</ID> <ID>LongParameterList:DriverDSL.kt$DriverDSL$( defaultParameters: NodeParameters = NodeParameters(), providedName: CordaX500Name? = defaultParameters.providedName, rpcUsers: List&lt;User&gt; = defaultParameters.rpcUsers, verifierType: VerifierType = defaultParameters.verifierType, customOverrides: Map&lt;String, Any?&gt; = defaultParameters.customOverrides, startInSameProcess: Boolean? = defaultParameters.startInSameProcess, maximumHeapSize: String = defaultParameters.maximumHeapSize )</ID>
<ID>LongParameterList:DriverDSL.kt$DriverDSL$( defaultParameters: NodeParameters = NodeParameters(), providedName: CordaX500Name? = defaultParameters.providedName, rpcUsers: List&lt;User&gt; = defaultParameters.rpcUsers, verifierType: VerifierType = defaultParameters.verifierType, customOverrides: Map&lt;String, Any?&gt; = defaultParameters.customOverrides, startInSameProcess: Boolean? = defaultParameters.startInSameProcess, maximumHeapSize: String = defaultParameters.maximumHeapSize, logLevelOverride: String? = defaultParameters.logLevelOverride )</ID> <ID>LongParameterList:DriverDSL.kt$DriverDSL$( defaultParameters: NodeParameters = NodeParameters(), providedName: CordaX500Name? = defaultParameters.providedName, rpcUsers: List&lt;User&gt; = defaultParameters.rpcUsers, verifierType: VerifierType = defaultParameters.verifierType, customOverrides: Map&lt;String, Any?&gt; = defaultParameters.customOverrides, startInSameProcess: Boolean? = defaultParameters.startInSameProcess, maximumHeapSize: String = defaultParameters.maximumHeapSize, logLevelOverride: String? = defaultParameters.logLevelOverride )</ID>
<ID>LongParameterList:DriverDSLImpl.kt$( isDebug: Boolean = DriverParameters().isDebug, driverDirectory: Path = DriverParameters().driverDirectory, portAllocation: PortAllocation = DriverParameters().portAllocation, debugPortAllocation: PortAllocation = DriverParameters().debugPortAllocation, systemProperties: Map&lt;String, String&gt; = DriverParameters().systemProperties, useTestClock: Boolean = DriverParameters().useTestClock, startNodesInProcess: Boolean = DriverParameters().startNodesInProcess, extraCordappPackagesToScan: List&lt;String&gt; = @Suppress("DEPRECATION") DriverParameters().extraCordappPackagesToScan, waitForAllNodesToFinish: Boolean = DriverParameters().waitForAllNodesToFinish, notarySpecs: List&lt;NotarySpec&gt; = DriverParameters().notarySpecs, jmxPolicy: JmxPolicy = DriverParameters().jmxPolicy, networkParameters: NetworkParameters = DriverParameters().networkParameters, compatibilityZone: CompatibilityZoneParams? = null, notaryCustomOverrides: Map&lt;String, Any?&gt; = DriverParameters().notaryCustomOverrides, inMemoryDB: Boolean = DriverParameters().inMemoryDB, cordappsForAllNodes: Collection&lt;TestCordappInternal&gt;? = null, dsl: DriverDSLImpl.() -&gt; A )</ID>
<ID>LongParameterList:DriverDSLImpl.kt$DriverDSLImpl.Companion$( config: NodeConfig, quasarJarPath: String, debugPort: Int?, overriddenSystemProperties: Map&lt;String, String&gt;, maximumHeapSize: String, logLevelOverride: String?, vararg extraCmdLineFlag: String )</ID>
<ID>LongParameterList:DummyFungibleContract.kt$DummyFungibleContract$(inputs: List&lt;State&gt;, outputs: List&lt;State&gt;, tx: LedgerTransaction, issueCommand: CommandWithParties&lt;Commands.Issue&gt;, currency: Currency, issuer: PartyAndReference)</ID> <ID>LongParameterList:DummyFungibleContract.kt$DummyFungibleContract$(inputs: List&lt;State&gt;, outputs: List&lt;State&gt;, tx: LedgerTransaction, issueCommand: CommandWithParties&lt;Commands.Issue&gt;, currency: Currency, issuer: PartyAndReference)</ID>
<ID>LongParameterList:IRS.kt$FloatingRatePaymentEvent$(date: LocalDate = this.date, accrualStartDate: LocalDate = this.accrualStartDate, accrualEndDate: LocalDate = this.accrualEndDate, dayCountBasisDay: DayCountBasisDay = this.dayCountBasisDay, dayCountBasisYear: DayCountBasisYear = this.dayCountBasisYear, fixingDate: LocalDate = this.fixingDate, notional: Amount&lt;Currency&gt; = this.notional, rate: Rate = this.rate)</ID> <ID>LongParameterList:IRS.kt$FloatingRatePaymentEvent$(date: LocalDate = this.date, accrualStartDate: LocalDate = this.accrualStartDate, accrualEndDate: LocalDate = this.accrualEndDate, dayCountBasisDay: DayCountBasisDay = this.dayCountBasisDay, dayCountBasisYear: DayCountBasisYear = this.dayCountBasisYear, fixingDate: LocalDate = this.fixingDate, notional: Amount&lt;Currency&gt; = this.notional, rate: Rate = this.rate)</ID>
<ID>LongParameterList:IRS.kt$InterestRateSwap$(floatingLeg: FloatingLeg, fixedLeg: FixedLeg, calculation: Calculation, common: Common, oracle: Party, notary: Party)</ID> <ID>LongParameterList:IRS.kt$InterestRateSwap$(floatingLeg: FloatingLeg, fixedLeg: FixedLeg, calculation: Calculation, common: Common, oracle: Party, notary: Party)</ID>
@ -729,8 +727,6 @@
<ID>LongParameterList:ParametersUtilities.kt$( notaries: List&lt;NotaryInfo&gt; = emptyList(), minimumPlatformVersion: Int = 1, modifiedTime: Instant = Instant.now(), maxMessageSize: Int = 10485760, // TODO: Make this configurable and consistence across driver, bootstrapper, demobench and NetworkMapServer maxTransactionSize: Int = maxMessageSize * 50, whitelistedContractImplementations: Map&lt;String, List&lt;AttachmentId&gt;&gt; = emptyMap(), epoch: Int = 1, eventHorizon: Duration = 30.days, packageOwnership: Map&lt;String, PublicKey&gt; = emptyMap() )</ID> <ID>LongParameterList:ParametersUtilities.kt$( notaries: List&lt;NotaryInfo&gt; = emptyList(), minimumPlatformVersion: Int = 1, modifiedTime: Instant = Instant.now(), maxMessageSize: Int = 10485760, // TODO: Make this configurable and consistence across driver, bootstrapper, demobench and NetworkMapServer maxTransactionSize: Int = maxMessageSize * 50, whitelistedContractImplementations: Map&lt;String, List&lt;AttachmentId&gt;&gt; = emptyMap(), epoch: Int = 1, eventHorizon: Duration = 30.days, packageOwnership: Map&lt;String, PublicKey&gt; = emptyMap() )</ID>
<ID>LongParameterList:PersistentUniquenessProvider.kt$PersistentUniquenessProvider$( states: List&lt;StateRef&gt;, txId: SecureHash, callerIdentity: Party, requestSignature: NotarisationRequestSignature, timeWindow: TimeWindow?, references: List&lt;StateRef&gt; )</ID> <ID>LongParameterList:PersistentUniquenessProvider.kt$PersistentUniquenessProvider$( states: List&lt;StateRef&gt;, txId: SecureHash, callerIdentity: Party, requestSignature: NotarisationRequestSignature, timeWindow: TimeWindow?, references: List&lt;StateRef&gt; )</ID>
<ID>LongParameterList:PhysicalLocationStructures.kt$WorldCoordinate$(screenWidth: Double, screenHeight: Double, topLatitude: Double, bottomLatitude: Double, leftLongitude: Double, rightLongitude: Double)</ID> <ID>LongParameterList:PhysicalLocationStructures.kt$WorldCoordinate$(screenWidth: Double, screenHeight: Double, topLatitude: Double, bottomLatitude: Double, leftLongitude: Double, rightLongitude: Double)</ID>
<ID>LongParameterList:ProcessUtilities.kt$ProcessUtilities$( arguments: List&lt;String&gt;, classPath: List&lt;String&gt; = defaultClassPath, workingDirectory: Path? = null, jdwpPort: Int? = null, extraJvmArguments: List&lt;String&gt; = emptyList(), maximumHeapSize: String? = null )</ID>
<ID>LongParameterList:ProcessUtilities.kt$ProcessUtilities$( className: String, arguments: List&lt;String&gt;, classPath: List&lt;String&gt; = defaultClassPath, workingDirectory: Path? = null, jdwpPort: Int? = null, extraJvmArguments: List&lt;String&gt; = emptyList(), maximumHeapSize: String? = null )</ID>
<ID>LongParameterList:QueryCriteria.kt$QueryCriteria.FungibleAssetQueryCriteria$( participants: List&lt;AbstractParty&gt;? = this.participants, owner: List&lt;AbstractParty&gt;? = this.owner, quantity: ColumnPredicate&lt;Long&gt;? = this.quantity, issuer: List&lt;AbstractParty&gt;? = this.issuer, issuerRef: List&lt;OpaqueBytes&gt;? = this.issuerRef, status: Vault.StateStatus = this.status, contractStateTypes: Set&lt;Class&lt;out ContractState&gt;&gt;? = this.contractStateTypes )</ID> <ID>LongParameterList:QueryCriteria.kt$QueryCriteria.FungibleAssetQueryCriteria$( participants: List&lt;AbstractParty&gt;? = this.participants, owner: List&lt;AbstractParty&gt;? = this.owner, quantity: ColumnPredicate&lt;Long&gt;? = this.quantity, issuer: List&lt;AbstractParty&gt;? = this.issuer, issuerRef: List&lt;OpaqueBytes&gt;? = this.issuerRef, status: Vault.StateStatus = this.status, contractStateTypes: Set&lt;Class&lt;out ContractState&gt;&gt;? = this.contractStateTypes )</ID>
<ID>LongParameterList:QueryCriteria.kt$QueryCriteria.FungibleAssetQueryCriteria$( participants: List&lt;AbstractParty&gt;? = this.participants, owner: List&lt;AbstractParty&gt;? = this.owner, quantity: ColumnPredicate&lt;Long&gt;? = this.quantity, issuer: List&lt;AbstractParty&gt;? = this.issuer, issuerRef: List&lt;OpaqueBytes&gt;? = this.issuerRef, status: Vault.StateStatus = this.status, contractStateTypes: Set&lt;Class&lt;out ContractState&gt;&gt;? = this.contractStateTypes, relevancyStatus: Vault.RelevancyStatus = this.relevancyStatus )</ID> <ID>LongParameterList:QueryCriteria.kt$QueryCriteria.FungibleAssetQueryCriteria$( participants: List&lt;AbstractParty&gt;? = this.participants, owner: List&lt;AbstractParty&gt;? = this.owner, quantity: ColumnPredicate&lt;Long&gt;? = this.quantity, issuer: List&lt;AbstractParty&gt;? = this.issuer, issuerRef: List&lt;OpaqueBytes&gt;? = this.issuerRef, status: Vault.StateStatus = this.status, contractStateTypes: Set&lt;Class&lt;out ContractState&gt;&gt;? = this.contractStateTypes, relevancyStatus: Vault.RelevancyStatus = this.relevancyStatus )</ID>
<ID>LongParameterList:QueryCriteria.kt$QueryCriteria.LinearStateQueryCriteria$( participants: List&lt;AbstractParty&gt;? = this.participants, uuid: List&lt;UUID&gt;? = this.uuid, externalId: List&lt;String&gt;? = this.externalId, status: Vault.StateStatus = this.status, contractStateTypes: Set&lt;Class&lt;out ContractState&gt;&gt;? = this.contractStateTypes, relevancyStatus: Vault.RelevancyStatus = this.relevancyStatus )</ID> <ID>LongParameterList:QueryCriteria.kt$QueryCriteria.LinearStateQueryCriteria$( participants: List&lt;AbstractParty&gt;? = this.participants, uuid: List&lt;UUID&gt;? = this.uuid, externalId: List&lt;String&gt;? = this.externalId, status: Vault.StateStatus = this.status, contractStateTypes: Set&lt;Class&lt;out ContractState&gt;&gt;? = this.contractStateTypes, relevancyStatus: Vault.RelevancyStatus = this.relevancyStatus )</ID>
@ -738,7 +734,6 @@
<ID>LongParameterList:QueryCriteria.kt$QueryCriteria.VaultQueryCriteria$( status: Vault.StateStatus = Vault.StateStatus.UNCONSUMED, contractStateTypes: Set&lt;Class&lt;out ContractState&gt;&gt;? = null, stateRefs: List&lt;StateRef&gt;? = null, notary: List&lt;AbstractParty&gt;? = null, softLockingCondition: SoftLockingCondition? = null, timeCondition: TimeCondition? = null, relevancyStatus: Vault.RelevancyStatus = Vault.RelevancyStatus.ALL, constraintTypes: Set&lt;Vault.ConstraintInfo.Type&gt; = emptySet(), constraints: Set&lt;Vault.ConstraintInfo&gt; = emptySet(), participants: List&lt;AbstractParty&gt;? = null, externalIds: List&lt;UUID&gt; = emptyList() )</ID> <ID>LongParameterList:QueryCriteria.kt$QueryCriteria.VaultQueryCriteria$( status: Vault.StateStatus = Vault.StateStatus.UNCONSUMED, contractStateTypes: Set&lt;Class&lt;out ContractState&gt;&gt;? = null, stateRefs: List&lt;StateRef&gt;? = null, notary: List&lt;AbstractParty&gt;? = null, softLockingCondition: SoftLockingCondition? = null, timeCondition: TimeCondition? = null, relevancyStatus: Vault.RelevancyStatus = Vault.RelevancyStatus.ALL, constraintTypes: Set&lt;Vault.ConstraintInfo.Type&gt; = emptySet(), constraints: Set&lt;Vault.ConstraintInfo&gt; = emptySet(), participants: List&lt;AbstractParty&gt;? = null, externalIds: List&lt;UUID&gt; = emptyList() )</ID>
<ID>LongParameterList:QueryCriteria.kt$QueryCriteria.VaultQueryCriteria$( status: Vault.StateStatus = this.status, contractStateTypes: Set&lt;Class&lt;out ContractState&gt;&gt;? = this.contractStateTypes, stateRefs: List&lt;StateRef&gt;? = this.stateRefs, notary: List&lt;AbstractParty&gt;? = this.notary, softLockingCondition: SoftLockingCondition? = this.softLockingCondition, timeCondition: TimeCondition? = this.timeCondition )</ID> <ID>LongParameterList:QueryCriteria.kt$QueryCriteria.VaultQueryCriteria$( status: Vault.StateStatus = this.status, contractStateTypes: Set&lt;Class&lt;out ContractState&gt;&gt;? = this.contractStateTypes, stateRefs: List&lt;StateRef&gt;? = this.stateRefs, notary: List&lt;AbstractParty&gt;? = this.notary, softLockingCondition: SoftLockingCondition? = this.softLockingCondition, timeCondition: TimeCondition? = this.timeCondition )</ID>
<ID>LongParameterList:RPCClient.kt$RPCClient$( rpcOpsClass: Class&lt;I&gt;, username: String, password: String, externalTrace: Trace? = null, impersonatedActor: Actor? = null, targetLegalIdentity: CordaX500Name? = null )</ID> <ID>LongParameterList:RPCClient.kt$RPCClient$( rpcOpsClass: Class&lt;I&gt;, username: String, password: String, externalTrace: Trace? = null, impersonatedActor: Actor? = null, targetLegalIdentity: CordaX500Name? = null )</ID>
<ID>LongParameterList:RPCDriver.kt$( isDebug: Boolean = false, driverDirectory: Path = Paths.get("build") / "rpc-driver" / getTimestampAsDirectoryName(), portAllocation: PortAllocation = globalPortAllocation, debugPortAllocation: PortAllocation = globalDebugPortAllocation, systemProperties: Map&lt;String, String&gt; = emptyMap(), useTestClock: Boolean = false, startNodesInProcess: Boolean = false, waitForNodesToFinish: Boolean = false, extraCordappPackagesToScan: List&lt;String&gt; = emptyList(), notarySpecs: List&lt;NotarySpec&gt; = emptyList(), externalTrace: Trace? = null, @Suppress("DEPRECATION") jmxPolicy: JmxPolicy = JmxPolicy(), networkParameters: NetworkParameters = testNetworkParameters(), notaryCustomOverrides: Map&lt;String, Any?&gt; = emptyMap(), inMemoryDB: Boolean = true, cordappsForAllNodes: Collection&lt;TestCordappInternal&gt;? = null, dsl: RPCDriverDSL.() -&gt; A )</ID>
<ID>LongParameterList:RPCDriver.kt$RPCDriverDSL$( rpcUser: User = rpcTestUser, nodeLegalName: CordaX500Name = fakeNodeLegalName, configuration: RPCServerConfiguration = RPCServerConfiguration.DEFAULT, listOps: List&lt;I&gt;, brokerHandle: RpcBrokerHandle, queueDrainTimeout: Duration = 5.seconds )</ID> <ID>LongParameterList:RPCDriver.kt$RPCDriverDSL$( rpcUser: User = rpcTestUser, nodeLegalName: CordaX500Name = fakeNodeLegalName, configuration: RPCServerConfiguration = RPCServerConfiguration.DEFAULT, listOps: List&lt;I&gt;, brokerHandle: RpcBrokerHandle, queueDrainTimeout: Duration = 5.seconds )</ID>
<ID>LongParameterList:RPCDriver.kt$RPCDriverDSL$( rpcUser: User = rpcTestUser, nodeLegalName: CordaX500Name = fakeNodeLegalName, configuration: RPCServerConfiguration = RPCServerConfiguration.DEFAULT, ops: I, brokerHandle: RpcBrokerHandle, queueDrainTimeout: Duration = 5.seconds )</ID> <ID>LongParameterList:RPCDriver.kt$RPCDriverDSL$( rpcUser: User = rpcTestUser, nodeLegalName: CordaX500Name = fakeNodeLegalName, configuration: RPCServerConfiguration = RPCServerConfiguration.DEFAULT, ops: I, brokerHandle: RpcBrokerHandle, queueDrainTimeout: Duration = 5.seconds )</ID>
<ID>LongParameterList:RPCDriver.kt$RPCDriverDSL$( rpcUser: User = rpcTestUser, nodeLegalName: CordaX500Name = fakeNodeLegalName, maxFileSize: Int = MAX_MESSAGE_SIZE, maxBufferedBytesPerClient: Long = 10L * MAX_MESSAGE_SIZE, configuration: RPCServerConfiguration = RPCServerConfiguration.DEFAULT, ops: I, queueDrainTimeout: Duration = 5.seconds )</ID> <ID>LongParameterList:RPCDriver.kt$RPCDriverDSL$( rpcUser: User = rpcTestUser, nodeLegalName: CordaX500Name = fakeNodeLegalName, maxFileSize: Int = MAX_MESSAGE_SIZE, maxBufferedBytesPerClient: Long = 10L * MAX_MESSAGE_SIZE, configuration: RPCServerConfiguration = RPCServerConfiguration.DEFAULT, ops: I, queueDrainTimeout: Duration = 5.seconds )</ID>
@ -1320,11 +1315,8 @@
<ID>MaxLineLength:AMQPClientSerializationScheme.kt$AMQPClientSerializationScheme.Companion$fun createSerializationEnv(classLoader: ClassLoader? = null, customSerializers: Set&lt;SerializationCustomSerializer&lt;*, *&gt;&gt; = emptySet(), serializationWhitelists: Set&lt;SerializationWhitelist&gt; = emptySet(), serializerFactoriesForContexts: MutableMap&lt;SerializationFactoryCacheKey, SerializerFactory&gt; = AccessOrderLinkedHashMap&lt;SerializationFactoryCacheKey, SerializerFactory&gt;(128).toSynchronised()): SerializationEnvironment</ID> <ID>MaxLineLength:AMQPClientSerializationScheme.kt$AMQPClientSerializationScheme.Companion$fun createSerializationEnv(classLoader: ClassLoader? = null, customSerializers: Set&lt;SerializationCustomSerializer&lt;*, *&gt;&gt; = emptySet(), serializationWhitelists: Set&lt;SerializationWhitelist&gt; = emptySet(), serializerFactoriesForContexts: MutableMap&lt;SerializationFactoryCacheKey, SerializerFactory&gt; = AccessOrderLinkedHashMap&lt;SerializationFactoryCacheKey, SerializerFactory&gt;(128).toSynchronised()): SerializationEnvironment</ID>
<ID>MaxLineLength:AMQPClientSerializationScheme.kt$AMQPClientSerializationScheme.Companion$nodeSerializationEnv = createSerializationEnv(classLoader, customSerializers, serializationWhitelists, serializerFactoriesForContexts)</ID> <ID>MaxLineLength:AMQPClientSerializationScheme.kt$AMQPClientSerializationScheme.Companion$nodeSerializationEnv = createSerializationEnv(classLoader, customSerializers, serializationWhitelists, serializerFactoriesForContexts)</ID>
<ID>MaxLineLength:AMQPClientSerializationScheme.kt$AMQPClientSerializationScheme.Companion$registerScheme(AMQPClientSerializationScheme(customSerializers, serializationWhitelists, serializerFactoriesForContexts))</ID> <ID>MaxLineLength:AMQPClientSerializationScheme.kt$AMQPClientSerializationScheme.Companion$registerScheme(AMQPClientSerializationScheme(customSerializers, serializationWhitelists, serializerFactoriesForContexts))</ID>
<ID>MaxLineLength:AMQPSerializationScheme.kt$AbstractAMQPSerializationScheme$@DeleteForDJVM constructor(cordapps: List&lt;Cordapp&gt;) : this(cordapps.customSerializers, cordapps.serializationWhitelists, AccessOrderLinkedHashMap&lt;SerializationFactoryCacheKey, SerializerFactory&gt;(128).toSynchronised())</ID>
<ID>MaxLineLength:AMQPSerializationScheme.kt$AbstractAMQPSerializationScheme$private</ID>
<ID>MaxLineLength:AMQPSerializationScheme.kt$AbstractAMQPSerializationScheme$val key = SerializationFactoryCacheKey(context.whitelist, context.deserializationClassLoader, context.preventDataLoss, context.customSerializers)</ID> <ID>MaxLineLength:AMQPSerializationScheme.kt$AbstractAMQPSerializationScheme$val key = SerializationFactoryCacheKey(context.whitelist, context.deserializationClassLoader, context.preventDataLoss, context.customSerializers)</ID>
<ID>MaxLineLength:AMQPSerializationScheme.kt$AbstractAMQPSerializationScheme${ // This is a hack introduced in version 3 to fix a spring boot issue - CORDA-1747. // It breaks the shell because it overwrites the CordappClassloader with the system classloader that doesn't know about any CorDapps. // In case a spring boot serialization issue with generics is found, a better solution needs to be found to address it. // var contextToUse = context // if (context.useCase == SerializationContext.UseCase.RPCClient) { // contextToUse = context.withClassLoader(getContextClassLoader()) // } val serializerFactory = getSerializerFactory(context) return DeserializationInput(serializerFactory).deserialize(byteSequence, clazz, context) }</ID> <ID>MaxLineLength:AMQPSerializationScheme.kt$AbstractAMQPSerializationScheme${ // This is a hack introduced in version 3 to fix a spring boot issue - CORDA-1747. // It breaks the shell because it overwrites the CordappClassloader with the system classloader that doesn't know about any CorDapps. // In case a spring boot serialization issue with generics is found, a better solution needs to be found to address it. // var contextToUse = context // if (context.useCase == SerializationContext.UseCase.RPCClient) { // contextToUse = context.withClassLoader(getContextClassLoader()) // } val serializerFactory = getSerializerFactory(context) return DeserializationInput(serializerFactory).deserialize(byteSequence, clazz, context) }</ID>
<ID>MaxLineLength:AMQPSerializationScheme.kt$AbstractAMQPSerializationScheme${ with(factory) { register(publicKeySerializer) register(net.corda.serialization.internal.amqp.custom.PrivateKeySerializer) register(net.corda.serialization.internal.amqp.custom.ThrowableSerializer(this)) register(net.corda.serialization.internal.amqp.custom.BigDecimalSerializer) register(net.corda.serialization.internal.amqp.custom.BigIntegerSerializer) register(net.corda.serialization.internal.amqp.custom.CurrencySerializer) register(net.corda.serialization.internal.amqp.custom.OpaqueBytesSubSequenceSerializer(this)) register(net.corda.serialization.internal.amqp.custom.InstantSerializer(this)) register(net.corda.serialization.internal.amqp.custom.DurationSerializer(this)) register(net.corda.serialization.internal.amqp.custom.LocalDateSerializer(this)) register(net.corda.serialization.internal.amqp.custom.LocalDateTimeSerializer(this)) register(net.corda.serialization.internal.amqp.custom.LocalTimeSerializer(this)) register(net.corda.serialization.internal.amqp.custom.ZonedDateTimeSerializer(this)) register(net.corda.serialization.internal.amqp.custom.ZoneIdSerializer(this)) register(net.corda.serialization.internal.amqp.custom.OffsetTimeSerializer(this)) register(net.corda.serialization.internal.amqp.custom.OffsetDateTimeSerializer(this)) register(net.corda.serialization.internal.amqp.custom.OptionalSerializer(this)) register(net.corda.serialization.internal.amqp.custom.YearSerializer(this)) register(net.corda.serialization.internal.amqp.custom.YearMonthSerializer(this)) register(net.corda.serialization.internal.amqp.custom.MonthDaySerializer(this)) register(net.corda.serialization.internal.amqp.custom.PeriodSerializer(this)) register(net.corda.serialization.internal.amqp.custom.ClassSerializer(this)) register(net.corda.serialization.internal.amqp.custom.X509CertificateSerializer) register(net.corda.serialization.internal.amqp.custom.X509CRLSerializer) register(net.corda.serialization.internal.amqp.custom.CertPathSerializer(this)) register(net.corda.serialization.internal.amqp.custom.StringBufferSerializer) register(net.corda.serialization.internal.amqp.custom.InputStreamSerializer) register(net.corda.serialization.internal.amqp.custom.BitSetSerializer(this)) register(net.corda.serialization.internal.amqp.custom.EnumSetSerializer(this)) register(net.corda.serialization.internal.amqp.custom.ContractAttachmentSerializer(this)) registerNonDeterministicSerializers(factory) } // This step is registering custom serializers, which have been added after node initialisation (i.e. via attachments during transaction verification). // Note: the order between the registration of customSerializers and cordappCustomSerializers must be preserved as-is. The reason is the following: // Currently, the serialization infrastructure does not support multiple versions of a class (the first one that is registered dominates). // As a result, when inside a context with attachments class loader, we prioritize serializers loaded on-demand from attachments to serializers that had been // loaded during node initialisation, by scanning the cordapps folder. context.customSerializers.forEach { customSerializer -&gt; factory.registerExternal(CorDappCustomSerializer(customSerializer, factory)) } cordappCustomSerializers.forEach { customSerializer -&gt; factory.registerExternal(CorDappCustomSerializer(customSerializer, factory)) } context.properties[ContextPropertyKeys.SERIALIZERS]?.apply { uncheckedCast&lt;Any, List&lt;CustomSerializer&lt;out Any&gt;&gt;&gt;(this).forEach { factory.register(it) } } }</ID>
<ID>MaxLineLength:AMQPSerializerFactories.kt$ fun createClassCarpenter(context: SerializationContext): ClassCarpenter</ID> <ID>MaxLineLength:AMQPSerializerFactories.kt$ fun createClassCarpenter(context: SerializationContext): ClassCarpenter</ID>
<ID>MaxLineLength:AMQPServer.kt$AMQPServer$server.group(bossGroup, workerGroup).channel(NioServerSocketChannel::class.java).option(ChannelOption.SO_BACKLOG, 100).handler(LoggingHandler(LogLevel.INFO)).childHandler(ServerChannelInitializer(this))</ID> <ID>MaxLineLength:AMQPServer.kt$AMQPServer$server.group(bossGroup, workerGroup).channel(NioServerSocketChannel::class.java).option(ChannelOption.SO_BACKLOG, 100).handler(LoggingHandler(LogLevel.INFO)).childHandler(ServerChannelInitializer(this))</ID>
<ID>MaxLineLength:AMQPServer.kt$AMQPServer$val channelFuture = server.bind(hostName, port).sync() // block/throw here as better to know we failed to claim port than carry on</ID> <ID>MaxLineLength:AMQPServer.kt$AMQPServer$val channelFuture = server.bind(hostName, port).sync() // block/throw here as better to know we failed to claim port than carry on</ID>
@ -1395,7 +1387,6 @@
<ID>MaxLineLength:AddressBindingFailureTests.kt$AddressBindingFailureTests$@Test fun `rpc admin address`()</ID> <ID>MaxLineLength:AddressBindingFailureTests.kt$AddressBindingFailureTests$@Test fun `rpc admin address`()</ID>
<ID>MaxLineLength:AddressBindingFailureTests.kt$AddressBindingFailureTests$assertThat(exception.addresses).contains(address).withFailMessage("Expected addresses to contain $address but was ${exception.addresses}.")</ID> <ID>MaxLineLength:AddressBindingFailureTests.kt$AddressBindingFailureTests$assertThat(exception.addresses).contains(address).withFailMessage("Expected addresses to contain $address but was ${exception.addresses}.")</ID>
<ID>MaxLineLength:AddressBindingFailureTests.kt$AddressBindingFailureTests$assertThatThrownBy { startNode(customOverrides = overrides(address)).getOrThrow() }</ID> <ID>MaxLineLength:AddressBindingFailureTests.kt$AddressBindingFailureTests$assertThatThrownBy { startNode(customOverrides = overrides(address)).getOrThrow() }</ID>
<ID>MaxLineLength:AddressBindingFailureTests.kt$AddressBindingFailureTests$driver</ID>
<ID>MaxLineLength:AliasPrivateKeyTest.kt$AliasPrivateKeyTest$signingCertStore.query { setPrivateKey(alias, aliasPrivateKey, listOf(NOT_YET_REGISTERED_MARKER_KEYS_AND_CERTS.ECDSAR1_CERT), "entrypassword") }</ID> <ID>MaxLineLength:AliasPrivateKeyTest.kt$AliasPrivateKeyTest$signingCertStore.query { setPrivateKey(alias, aliasPrivateKey, listOf(NOT_YET_REGISTERED_MARKER_KEYS_AND_CERTS.ECDSAR1_CERT), "entrypassword") }</ID>
<ID>MaxLineLength:AliasPrivateKeyTest.kt$AliasPrivateKeyTest$val signingCertStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory, "keystorepass").get(createNew = true)</ID> <ID>MaxLineLength:AliasPrivateKeyTest.kt$AliasPrivateKeyTest$val signingCertStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory, "keystorepass").get(createNew = true)</ID>
<ID>MaxLineLength:AliasPrivateKeyTest.kt$AliasPrivateKeyTest${ val alias = "01234567890" val aliasPrivateKey = AliasPrivateKey(alias) val certificatesDirectory = tempFolder.root.toPath() val signingCertStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory, "keystorepass").get(createNew = true) signingCertStore.query { setPrivateKey(alias, aliasPrivateKey, listOf(NOT_YET_REGISTERED_MARKER_KEYS_AND_CERTS.ECDSAR1_CERT), "entrypassword") } // We can retrieve the certificate. assertTrue { signingCertStore.contains(alias) } // We can retrieve the certificate. assertEquals(NOT_YET_REGISTERED_MARKER_KEYS_AND_CERTS.ECDSAR1_CERT, signingCertStore[alias]) // Although we can store an AliasPrivateKey, we cannot retrieve it. But, it's fine as we use certStore for storing/handling certs only. assertThatIllegalArgumentException().isThrownBy { signingCertStore.query { getPrivateKey(alias, "entrypassword") } }.withMessage("Unrecognised algorithm: 1.3.6.1.4.1.50530.1.2") }</ID> <ID>MaxLineLength:AliasPrivateKeyTest.kt$AliasPrivateKeyTest${ val alias = "01234567890" val aliasPrivateKey = AliasPrivateKey(alias) val certificatesDirectory = tempFolder.root.toPath() val signingCertStore = CertificateStoreStubs.Signing.withCertificatesDirectory(certificatesDirectory, "keystorepass").get(createNew = true) signingCertStore.query { setPrivateKey(alias, aliasPrivateKey, listOf(NOT_YET_REGISTERED_MARKER_KEYS_AND_CERTS.ECDSAR1_CERT), "entrypassword") } // We can retrieve the certificate. assertTrue { signingCertStore.contains(alias) } // We can retrieve the certificate. assertEquals(NOT_YET_REGISTERED_MARKER_KEYS_AND_CERTS.ECDSAR1_CERT, signingCertStore[alias]) // Although we can store an AliasPrivateKey, we cannot retrieve it. But, it's fine as we use certStore for storing/handling certs only. assertThatIllegalArgumentException().isThrownBy { signingCertStore.query { getPrivateKey(alias, "entrypassword") } }.withMessage("Unrecognised algorithm: 1.3.6.1.4.1.50530.1.2") }</ID>
@ -1542,7 +1533,6 @@
<ID>MaxLineLength:BaseTransactions.kt$FullTransaction$"Notary ($notaryParty) specified by the transaction is not on the network parameter whitelist: [${notaryWhitelist.joinToString()}]"</ID> <ID>MaxLineLength:BaseTransactions.kt$FullTransaction$"Notary ($notaryParty) specified by the transaction is not on the network parameter whitelist: [${notaryWhitelist.joinToString()}]"</ID>
<ID>MaxLineLength:BaseTransactions.kt$FullTransaction$/** * Network parameters that were in force when this transaction was created. Resolved from the hash of network parameters on the corresponding * wire transaction. */ abstract val networkParameters: NetworkParameters?</ID> <ID>MaxLineLength:BaseTransactions.kt$FullTransaction$/** * Network parameters that were in force when this transaction was created. Resolved from the hash of network parameters on the corresponding * wire transaction. */ abstract val networkParameters: NetworkParameters?</ID>
<ID>MaxLineLength:BasicHSMKeyManagementService.kt$BasicHSMKeyManagementService$require(it.private is AliasPrivateKey) { "${this.javaClass.name} supports AliasPrivateKeys only, but ${it.private.algorithm} key was found" }</ID> <ID>MaxLineLength:BasicHSMKeyManagementService.kt$BasicHSMKeyManagementService$require(it.private is AliasPrivateKey) { "${this.javaClass.name} supports AliasPrivateKeys only, but ${it.private.algorithm} key was found" }</ID>
<ID>MaxLineLength:BitSetSerializer.kt$BitSetSerializer : Proxy</ID>
<ID>MaxLineLength:BlobInspector.kt$BlobInspector$@Option(names = ["--input-format"], paramLabel = "type", description = ["Input format. If the file can't be decoded with the given value it's auto-detected, so you should never normally need to specify this. Possible values: [BINARY, HEX, BASE64]"])</ID> <ID>MaxLineLength:BlobInspector.kt$BlobInspector$@Option(names = ["--input-format"], paramLabel = "type", description = ["Input format. If the file can't be decoded with the given value it's auto-detected, so you should never normally need to specify this. Possible values: [BINARY, HEX, BASE64]"])</ID>
<ID>MaxLineLength:BootTests.kt$BootTests$val logFile = logFolder.list { it.filter { a -&gt; a.isRegularFile() &amp;&amp; a.fileName.toString().startsWith("node") }.findFirst().get() }</ID> <ID>MaxLineLength:BootTests.kt$BootTests$val logFile = logFolder.list { it.filter { a -&gt; a.isRegularFile() &amp;&amp; a.fileName.toString().startsWith("node") }.findFirst().get() }</ID>
<ID>MaxLineLength:BootstrapperView.kt$BootstrapperView$return Pair(mapOf(Constants.REGION_ARG_NAME to ChoiceDialog&lt;Region&gt;(Region.EUROPE_WEST, Region.values().toList().sortedBy { it.name() }).showAndWait().get().name()), networkName1)</ID> <ID>MaxLineLength:BootstrapperView.kt$BootstrapperView$return Pair(mapOf(Constants.REGION_ARG_NAME to ChoiceDialog&lt;Region&gt;(Region.EUROPE_WEST, Region.values().toList().sortedBy { it.name() }).showAndWait().get().name()), networkName1)</ID>
@ -1774,7 +1764,6 @@
<ID>MaxLineLength:ConfigUtilities.kt$ // TODO Move this to KeyStoreConfigHelpers. fun NodeConfiguration.configureWithDevSSLCertificate(cryptoService: CryptoService? = null)</ID> <ID>MaxLineLength:ConfigUtilities.kt$ // TODO Move this to KeyStoreConfigHelpers. fun NodeConfiguration.configureWithDevSSLCertificate(cryptoService: CryptoService? = null)</ID>
<ID>MaxLineLength:ConfigUtilities.kt$// Problems: // - Forces you to have a primary constructor with all fields of name and type matching the configuration file structure. // - Encourages weak bean-like types. // - Cannot support a many-to-one relationship between configuration file structures and configuration domain type. This is essential for versioning of the configuration files. // - It's complicated and based on reflection, meaning problems with it are typically found at runtime. // - It doesn't support validation errors in a structured way. If something goes wrong, it throws exceptions, which doesn't support good usability practices like displaying all the errors at once. fun &lt;T : Any&gt; Config.parseAs(clazz: KClass&lt;T&gt;, onUnknownKeys: ((Set&lt;String&gt;, logger: Logger) -&gt; Unit) = UnknownConfigKeysPolicy.FAIL::handle, nestedPath: String? = null, baseDirectory: Path? = null): T</ID> <ID>MaxLineLength:ConfigUtilities.kt$// Problems: // - Forces you to have a primary constructor with all fields of name and type matching the configuration file structure. // - Encourages weak bean-like types. // - Cannot support a many-to-one relationship between configuration file structures and configuration domain type. This is essential for versioning of the configuration files. // - It's complicated and based on reflection, meaning problems with it are typically found at runtime. // - It doesn't support validation errors in a structured way. If something goes wrong, it throws exceptions, which doesn't support good usability practices like displaying all the errors at once. fun &lt;T : Any&gt; Config.parseAs(clazz: KClass&lt;T&gt;, onUnknownKeys: ((Set&lt;String&gt;, logger: Logger) -&gt; Unit) = UnknownConfigKeysPolicy.FAIL::handle, nestedPath: String? = null, baseDirectory: Path? = null): T</ID>
<ID>MaxLineLength:ConfigUtilities.kt$// TODO Move this to KeyStoreConfigHelpers. fun MutualSslConfiguration.configureDevKeyAndTrustStores(myLegalName: CordaX500Name, signingCertificateStore: FileBasedCertificateStoreSupplier, certificatesDirectory: Path, cryptoService: CryptoService? = null)</ID> <ID>MaxLineLength:ConfigUtilities.kt$// TODO Move this to KeyStoreConfigHelpers. fun MutualSslConfiguration.configureDevKeyAndTrustStores(myLegalName: CordaX500Name, signingCertificateStore: FileBasedCertificateStoreSupplier, certificatesDirectory: Path, cryptoService: CryptoService? = null)</ID>
<ID>MaxLineLength:ConfigUtilities.kt$ConfigHelper$return ConfigFactory.parseMap(toProperties().filterKeys { (it as String).startsWith(CORDA_PROPERTY_PREFIX) }.mapKeys { (it.key as String).removePrefix(CORDA_PROPERTY_PREFIX) })</ID>
<ID>MaxLineLength:ConfigUtilities.kt$ConfigHelper$val smartDevMode = CordaSystemUtils.isOsMac() || (CordaSystemUtils.isOsWindows() &amp;&amp; !CordaSystemUtils.getOsName().toLowerCase().contains("server"))</ID> <ID>MaxLineLength:ConfigUtilities.kt$ConfigHelper$val smartDevMode = CordaSystemUtils.isOsMac() || (CordaSystemUtils.isOsWindows() &amp;&amp; !CordaSystemUtils.getOsName().toLowerCase().contains("server"))</ID>
<ID>MaxLineLength:ConfigUtilities.kt$fun Any?.toConfigValue(): ConfigValue</ID> <ID>MaxLineLength:ConfigUtilities.kt$fun Any?.toConfigValue(): ConfigValue</ID>
<ID>MaxLineLength:ConfigUtilities.kt$inline fun &lt;reified T : Any&gt; Config.parseAs(noinline onUnknownKeys: ((Set&lt;String&gt;, logger: Logger) -&gt; Unit) = UnknownConfigKeysPolicy.FAIL::handle): T</ID> <ID>MaxLineLength:ConfigUtilities.kt$inline fun &lt;reified T : Any&gt; Config.parseAs(noinline onUnknownKeys: ((Set&lt;String&gt;, logger: Logger) -&gt; Unit) = UnknownConfigKeysPolicy.FAIL::handle): T</ID>
@ -1899,8 +1888,6 @@
<ID>MaxLineLength:ConstraintsUtils.kt$input.isAutomaticHashConstraint() || output.isAutomaticHashConstraint() -&gt; throw IllegalArgumentException("Illegal constraint: AutomaticHashConstraint.")</ID> <ID>MaxLineLength:ConstraintsUtils.kt$input.isAutomaticHashConstraint() || output.isAutomaticHashConstraint() -&gt; throw IllegalArgumentException("Illegal constraint: AutomaticHashConstraint.")</ID>
<ID>MaxLineLength:ConstraintsUtils.kt$when { // These branches should not happen, as this has been already checked. input is AutomaticPlaceholderConstraint || output is AutomaticPlaceholderConstraint -&gt; throw IllegalArgumentException("Illegal constraint: AutomaticPlaceholderConstraint.") input.isAutomaticHashConstraint() || output.isAutomaticHashConstraint() -&gt; throw IllegalArgumentException("Illegal constraint: AutomaticHashConstraint.") // Transition to the same constraint. input == output -&gt; true // You can't transition from the AlwaysAcceptAttachmentConstraint to anything else, as it could hide something illegal. input is AlwaysAcceptAttachmentConstraint &amp;&amp; output !is AlwaysAcceptAttachmentConstraint -&gt; false // Nothing can be migrated from the HashConstraint except a HashConstraint with the same Hash. (This check is redundant, but added for clarity) input is HashAttachmentConstraint &amp;&amp; output is HashAttachmentConstraint -&gt; input == output // Anything (except the AlwaysAcceptAttachmentConstraint) can be transformed to a HashAttachmentConstraint. input !is HashAttachmentConstraint &amp;&amp; output is HashAttachmentConstraint -&gt; true // The SignatureAttachmentConstraint allows migration from a Signature constraint with the same key. // TODO - we don't support currently third party signers. When we do, the output key will have to be stronger then the input key. input is SignatureAttachmentConstraint &amp;&amp; output is SignatureAttachmentConstraint -&gt; input.key == output.key // HashAttachmentConstraint can be transformed to a SignatureAttachmentConstraint when hash constraint verification checking disabled. HashAttachmentConstraint.disableHashConstraints &amp;&amp; input is HashAttachmentConstraint &amp;&amp; output is SignatureAttachmentConstraint -&gt; true // You can transition from the WhitelistConstraint to the SignatureConstraint only if all signers of the JAR are required to sign in the future. input is WhitelistedByZoneAttachmentConstraint &amp;&amp; output is SignatureAttachmentConstraint -&gt; attachment.signerKeys.isNotEmpty() &amp;&amp; output.key.keys.containsAll(attachment.signerKeys) else -&gt; false }</ID> <ID>MaxLineLength:ConstraintsUtils.kt$when { // These branches should not happen, as this has been already checked. input is AutomaticPlaceholderConstraint || output is AutomaticPlaceholderConstraint -&gt; throw IllegalArgumentException("Illegal constraint: AutomaticPlaceholderConstraint.") input.isAutomaticHashConstraint() || output.isAutomaticHashConstraint() -&gt; throw IllegalArgumentException("Illegal constraint: AutomaticHashConstraint.") // Transition to the same constraint. input == output -&gt; true // You can't transition from the AlwaysAcceptAttachmentConstraint to anything else, as it could hide something illegal. input is AlwaysAcceptAttachmentConstraint &amp;&amp; output !is AlwaysAcceptAttachmentConstraint -&gt; false // Nothing can be migrated from the HashConstraint except a HashConstraint with the same Hash. (This check is redundant, but added for clarity) input is HashAttachmentConstraint &amp;&amp; output is HashAttachmentConstraint -&gt; input == output // Anything (except the AlwaysAcceptAttachmentConstraint) can be transformed to a HashAttachmentConstraint. input !is HashAttachmentConstraint &amp;&amp; output is HashAttachmentConstraint -&gt; true // The SignatureAttachmentConstraint allows migration from a Signature constraint with the same key. // TODO - we don't support currently third party signers. When we do, the output key will have to be stronger then the input key. input is SignatureAttachmentConstraint &amp;&amp; output is SignatureAttachmentConstraint -&gt; input.key == output.key // HashAttachmentConstraint can be transformed to a SignatureAttachmentConstraint when hash constraint verification checking disabled. HashAttachmentConstraint.disableHashConstraints &amp;&amp; input is HashAttachmentConstraint &amp;&amp; output is SignatureAttachmentConstraint -&gt; true // You can transition from the WhitelistConstraint to the SignatureConstraint only if all signers of the JAR are required to sign in the future. input is WhitelistedByZoneAttachmentConstraint &amp;&amp; output is SignatureAttachmentConstraint -&gt; attachment.signerKeys.isNotEmpty() &amp;&amp; output.key.keys.containsAll(attachment.signerKeys) else -&gt; false }</ID>
<ID>MaxLineLength:ContractAttachment.kt$ContractAttachment$return "ContractAttachment(attachment=${attachment.id}, contracts='$allContracts', uploader='$uploader', signed='$isSigned', version='$version')"</ID> <ID>MaxLineLength:ContractAttachment.kt$ContractAttachment$return "ContractAttachment(attachment=${attachment.id}, contracts='$allContracts', uploader='$uploader', signed='$isSigned', version='$version')"</ID>
<ID>MaxLineLength:ContractAttachmentSerializer.kt$ContractAttachmentSerializer$return ContractAttachmentProxy(GeneratedAttachment(bytes, obj.uploader), obj.contract, obj.additionalContracts, obj.uploader, obj.signerKeys, obj.version)</ID>
<ID>MaxLineLength:ContractAttachmentSerializer.kt$ContractAttachmentSerializer.ContractAttachmentProxy$@KeepForDJVM data</ID>
<ID>MaxLineLength:ContractHierarchyTest.kt$ContractHierarchyTest$PrepareTransaction : FlowLogic</ID> <ID>MaxLineLength:ContractHierarchyTest.kt$ContractHierarchyTest$PrepareTransaction : FlowLogic</ID>
<ID>MaxLineLength:ContractHierarchyTest.kt$ContractHierarchyTest$mockNet = InternalMockNetwork(networkSendManuallyPumped = false, threadPerNode = true, cordappsForAllNodes = listOf(enclosedCordapp()))</ID> <ID>MaxLineLength:ContractHierarchyTest.kt$ContractHierarchyTest$mockNet = InternalMockNetwork(networkSendManuallyPumped = false, threadPerNode = true, cordappsForAllNodes = listOf(enclosedCordapp()))</ID>
<ID>MaxLineLength:ContractJarTestUtils.kt$ContractJarTestUtils$@JvmOverloads fun makeTestContractJar(workingDir: Path, contractNames: List&lt;String&gt;, signed: Boolean = false, version: Int = 1, generateManifest: Boolean = true, jarFileName : String? = null): Path</ID> <ID>MaxLineLength:ContractJarTestUtils.kt$ContractJarTestUtils$@JvmOverloads fun makeTestContractJar(workingDir: Path, contractNames: List&lt;String&gt;, signed: Boolean = false, version: Int = 1, generateManifest: Boolean = true, jarFileName : String? = null): Path</ID>
@ -2010,7 +1997,6 @@
<ID>MaxLineLength:CordappContext.kt$CordappContext.EmptyCordappConfig$override fun getLong(path: String)</ID> <ID>MaxLineLength:CordappContext.kt$CordappContext.EmptyCordappConfig$override fun getLong(path: String)</ID>
<ID>MaxLineLength:CordappContext.kt$CordappContext.EmptyCordappConfig$override fun getNumber(path: String)</ID> <ID>MaxLineLength:CordappContext.kt$CordappContext.EmptyCordappConfig$override fun getNumber(path: String)</ID>
<ID>MaxLineLength:CordappContext.kt$CordappContext.EmptyCordappConfig$override fun getString(path: String)</ID> <ID>MaxLineLength:CordappContext.kt$CordappContext.EmptyCordappConfig$override fun getString(path: String)</ID>
<ID>MaxLineLength:CordappResolver.kt$CordappResolver$logger.error("ATTENTION: More than one CorDapp installed on the node for contract $className. Please remove the previous version when upgrading to a new version.")</ID>
<ID>MaxLineLength:CordappResolverTest.kt$CordappResolverTest$@Test fun `when the same cordapp is registered for the same class multiple times, the resolver deduplicates and returns it as the current one`()</ID> <ID>MaxLineLength:CordappResolverTest.kt$CordappResolverTest$@Test fun `when the same cordapp is registered for the same class multiple times, the resolver deduplicates and returns it as the current one`()</ID>
<ID>MaxLineLength:CordappSmokeTest.kt$CordappSmokeTest$(additionalNodeInfoDir / "nodeInfo-41408E093F95EAD51F6892C34DEB65AE1A3569A4B0E5744769A1B485AF8E04B5").write(signedNodeInfo.serialize().bytes)</ID> <ID>MaxLineLength:CordappSmokeTest.kt$CordappSmokeTest$(additionalNodeInfoDir / "nodeInfo-41408E093F95EAD51F6892C34DEB65AE1A3569A4B0E5744769A1B485AF8E04B5").write(signedNodeInfo.serialize().bytes)</ID>
<ID>MaxLineLength:CordappSmokeTest.kt$CordappSmokeTest$val nodeInfo = createNodeInfoWithSingleIdentity(CordaX500Name(organisation = "Bob Corp", locality = "Madrid", country = "ES"), dummyKeyPair, dummyKeyPair.public)</ID> <ID>MaxLineLength:CordappSmokeTest.kt$CordappSmokeTest$val nodeInfo = createNodeInfoWithSingleIdentity(CordaX500Name(organisation = "Bob Corp", locality = "Madrid", country = "ES"), dummyKeyPair, dummyKeyPair.public)</ID>
@ -2096,9 +2082,8 @@
<ID>MaxLineLength:DriverDSLImpl.kt$DriverDSLImpl$private</ID> <ID>MaxLineLength:DriverDSLImpl.kt$DriverDSLImpl$private</ID>
<ID>MaxLineLength:DriverDSLImpl.kt$DriverDSLImpl$val flowOverrideConfig = FlowOverrideConfig(parameters.flowOverrides.map { FlowOverride(it.key.canonicalName, it.value.canonicalName) })</ID> <ID>MaxLineLength:DriverDSLImpl.kt$DriverDSLImpl$val flowOverrideConfig = FlowOverrideConfig(parameters.flowOverrides.map { FlowOverride(it.key.canonicalName, it.value.canonicalName) })</ID>
<ID>MaxLineLength:DriverDSLImpl.kt$DriverDSLImpl$val jdbcUrl = "jdbc:h2:mem:persistence${inMemoryCounter.getAndIncrement()};DB_CLOSE_ON_EXIT=FALSE;LOCK_TIMEOUT=10000;WRITE_DELAY=100"</ID> <ID>MaxLineLength:DriverDSLImpl.kt$DriverDSLImpl$val jdbcUrl = "jdbc:h2:mem:persistence${inMemoryCounter.getAndIncrement()};DB_CLOSE_ON_EXIT=FALSE;LOCK_TIMEOUT=10000;WRITE_DELAY=100"</ID>
<ID>MaxLineLength:DriverDSLImpl.kt$DriverDSLImpl$val process = startOutOfProcessNode(config, quasarJarPath, debugPort, systemProperties, parameters.maximumHeapSize, parameters.logLevelOverride)</ID>
<ID>MaxLineLength:DriverDSLImpl.kt$DriverDSLImpl.Companion$private operator fun Config.plus(property: Pair&lt;String, Any&gt;)</ID> <ID>MaxLineLength:DriverDSLImpl.kt$DriverDSLImpl.Companion$private operator fun Config.plus(property: Pair&lt;String, Any&gt;)</ID>
<ID>MaxLineLength:DriverDSLImpl.kt$DriverDSLImpl.Companion${ log.info("Starting out-of-process Node ${config.corda.myLegalName.organisation}, debug port is " + (debugPort ?: "not enabled")) // Write node.conf writeConfig(config.corda.baseDirectory, "node.conf", config.typesafe.toNodeOnly()) val systemProperties = mutableMapOf( "name" to config.corda.myLegalName, "visualvm.display.name" to "corda-${config.corda.myLegalName}" ) debugPort?.let { systemProperties += "log4j2.level" to "debug" systemProperties += "log4j2.debug" to "true" } systemProperties += inheritFromParentProcess() systemProperties += overriddenSystemProperties // See experimental/quasar-hook/README.md for how to generate. val excludePattern = "x(antlr**;bftsmart**;ch**;co.paralleluniverse**;com.codahale**;com.esotericsoftware**;" + "com.fasterxml**;com.google**;com.ibm**;com.intellij**;com.jcabi**;com.nhaarman**;com.opengamma**;" + "com.typesafe**;com.zaxxer**;de.javakaffee**;groovy**;groovyjarjarantlr**;groovyjarjarasm**;io.atomix**;" + "io.github**;io.netty**;jdk**;joptsimple**;junit**;kotlin**;net.bytebuddy**;net.i2p**;org.apache**;" + "org.assertj**;org.bouncycastle**;org.codehaus**;org.crsh**;org.dom4j**;org.fusesource**;org.h2**;" + "org.hamcrest**;org.hibernate**;org.jboss**;org.jcp**;org.joda**;org.junit**;org.mockito**;org.objectweb**;" + "org.objenesis**;org.slf4j**;org.w3c**;org.xml**;org.yaml**;reflectasm**;rx**;org.jolokia**;" + "com.lmax**;picocli**;liquibase**;com.github.benmanes**;org.json**;org.postgresql**;nonapi.io.github.classgraph**;)" val extraJvmArguments = systemProperties.removeResolvedClasspath().map { "-D${it.key}=${it.value}" } + "-javaagent:$quasarJarPath=$excludePattern" val loggingLevel = when { logLevelOverride != null -&gt; logLevelOverride debugPort == null -&gt; "INFO" else -&gt; "DEBUG" } val arguments = mutableListOf( "--base-directory=${config.corda.baseDirectory}", "--logging-level=$loggingLevel", "--no-local-shell").also { it += extraCmdLineFlag }.toList() // The following dependencies are excluded from the classpath of the created JVM, so that the environment resembles a real one as close as possible. // These are either classes that will be added as attachments to the node (i.e. samples, finance, opengamma etc.) or irrelevant testing libraries (test, corda-mock etc.). // TODO: There is pending work to fix this issue without custom blacklisting. See: https://r3-cev.atlassian.net/browse/CORDA-2164. val exclude = listOf("samples", "finance", "integrationTest", "test", "corda-mock", "com.opengamma.strata") val cp = ProcessUtilities.defaultClassPath.filterNot { cpEntry -&gt; exclude.any { token -&gt; cpEntry.contains("${File.separatorChar}$token") } || cpEntry.endsWith("-tests.jar") } return ProcessUtilities.startJavaProcess( className = "net.corda.node.Corda", // cannot directly get class for this, so just use string arguments = arguments, jdwpPort = debugPort, extraJvmArguments = extraJvmArguments, workingDirectory = config.corda.baseDirectory, maximumHeapSize = maximumHeapSize, classPath = cp ) }</ID> <ID>MaxLineLength:DriverDSLImpl.kt$DriverDSLImpl.Companion${ log.info("Starting out-of-process Node ${config.corda.myLegalName.organisation}, " + "debug port is " + (debugPort ?: "not enabled")) // Write node.conf writeConfig(config.corda.baseDirectory, "node.conf", config.typesafe.toNodeOnly()) val systemProperties = mutableMapOf( "name" to config.corda.myLegalName, "visualvm.display.name" to "corda-${config.corda.myLegalName}" ) debugPort?.let { systemProperties += "log4j2.level" to "debug" systemProperties += "log4j2.debug" to "true" } systemProperties += inheritFromParentProcess() systemProperties += overriddenSystemProperties // See experimental/quasar-hook/README.md for how to generate. val excludePattern = "x(antlr**;bftsmart**;ch**;co.paralleluniverse**;com.codahale**;com.esotericsoftware**;" + "com.fasterxml**;com.google**;com.ibm**;com.intellij**;com.jcabi**;com.nhaarman**;com.opengamma**;" + "com.typesafe**;com.zaxxer**;de.javakaffee**;groovy**;groovyjarjarantlr**;groovyjarjarasm**;io.atomix**;" + "io.github**;io.netty**;jdk**;joptsimple**;junit**;kotlin**;net.bytebuddy**;net.i2p**;org.apache**;" + "org.assertj**;org.bouncycastle**;org.codehaus**;org.crsh**;org.dom4j**;org.fusesource**;org.h2**;" + "org.hamcrest**;org.hibernate**;org.jboss**;org.jcp**;org.joda**;org.junit**;org.mockito**;org.objectweb**;" + "org.objenesis**;org.slf4j**;org.w3c**;org.xml**;org.yaml**;reflectasm**;rx**;org.jolokia**;" + "com.lmax**;picocli**;liquibase**;com.github.benmanes**;org.json**;org.postgresql**;nonapi.io.github.classgraph**;)" val extraJvmArguments = systemProperties.removeResolvedClasspath().map { "-D${it.key}=${it.value}" } + "-javaagent:$quasarJarPath=$excludePattern" val loggingLevel = when { logLevelOverride != null -&gt; logLevelOverride debugPort == null -&gt; "INFO" else -&gt; "DEBUG" } val arguments = mutableListOf( "--base-directory=${config.corda.baseDirectory}", "--logging-level=$loggingLevel", "--no-local-shell").also { it.addAll(extraCmdLineFlag) }.toList() // The following dependencies are excluded from the classpath of the created JVM, so that the environment resembles a real one as close as possible. // These are either classes that will be added as attachments to the node (i.e. samples, finance, opengamma etc.) or irrelevant testing libraries (test, corda-mock etc.). // TODO: There is pending work to fix this issue without custom blacklisting. See: https://r3-cev.atlassian.net/browse/CORDA-2164. val exclude = listOf("samples", "finance", "integrationTest", "test", "corda-mock", "com.opengamma.strata") val cp = ProcessUtilities.defaultClassPath.filterNot { cpEntry -&gt; exclude.any { token -&gt; cpEntry.contains("${File.separatorChar}$token") } || cpEntry.endsWith("-tests.jar") } return ProcessUtilities.startJavaProcess( className = "net.corda.node.Corda", // cannot directly get class for this, so just use string arguments = arguments, jdwpPort = debugPort, extraJvmArguments = extraJvmArguments, workingDirectory = config.corda.baseDirectory, maximumHeapSize = maximumHeapSize, classPath = cp, environmentVariables = environmentVariables ) }</ID>
<ID>MaxLineLength:DriverDSLImpl.kt$InternalDriverDSL$ fun &lt;A&gt; pollUntilNonNull(pollName: String, pollInterval: Duration = DEFAULT_POLL_INTERVAL, warnCount: Int = DEFAULT_WARN_COUNT, check: () -&gt; A?): CordaFuture&lt;A&gt;</ID> <ID>MaxLineLength:DriverDSLImpl.kt$InternalDriverDSL$ fun &lt;A&gt; pollUntilNonNull(pollName: String, pollInterval: Duration = DEFAULT_POLL_INTERVAL, warnCount: Int = DEFAULT_WARN_COUNT, check: () -&gt; A?): CordaFuture&lt;A&gt;</ID>
<ID>MaxLineLength:DriverDSLImpl.kt$InternalDriverDSL$ fun pollUntilTrue(pollName: String, pollInterval: Duration = DEFAULT_POLL_INTERVAL, warnCount: Int = DEFAULT_WARN_COUNT, check: () -&gt; Boolean): CordaFuture&lt;Unit&gt;</ID> <ID>MaxLineLength:DriverDSLImpl.kt$InternalDriverDSL$ fun pollUntilTrue(pollName: String, pollInterval: Duration = DEFAULT_POLL_INTERVAL, warnCount: Int = DEFAULT_WARN_COUNT, check: () -&gt; Boolean): CordaFuture&lt;Unit&gt;</ID>
<ID>MaxLineLength:DriverDSLImpl.kt$fun DriverDSL.startNode(providedName: CordaX500Name, devMode: Boolean, parameters: NodeParameters = NodeParameters()): CordaFuture&lt;NodeHandle&gt;</ID> <ID>MaxLineLength:DriverDSLImpl.kt$fun DriverDSL.startNode(providedName: CordaX500Name, devMode: Boolean, parameters: NodeParameters = NodeParameters()): CordaFuture&lt;NodeHandle&gt;</ID>
@ -2117,12 +2102,10 @@
<ID>MaxLineLength:DummyLinearStateSchemaV1.kt$DummyLinearStateSchemaV1.PersistentDummyLinearState$@Table(name = "dummy_linear_states", indexes = [Index(name = "external_id_idx", columnList = "external_id"), Index(name = "uuid_idx", columnList = "uuid")])</ID> <ID>MaxLineLength:DummyLinearStateSchemaV1.kt$DummyLinearStateSchemaV1.PersistentDummyLinearState$@Table(name = "dummy_linear_states", indexes = [Index(name = "external_id_idx", columnList = "external_id"), Index(name = "uuid_idx", columnList = "uuid")])</ID>
<ID>MaxLineLength:DummyLinearStateSchemaV2.kt$DummyLinearStateSchemaV2.PersistentDummyLinearState$@CollectionTable(name = "dummy_linear_states_v2_parts", joinColumns = [(JoinColumn(name = "output_index", referencedColumnName = "output_index")), (JoinColumn(name = "transaction_id", referencedColumnName = "transaction_id"))])</ID> <ID>MaxLineLength:DummyLinearStateSchemaV2.kt$DummyLinearStateSchemaV2.PersistentDummyLinearState$@CollectionTable(name = "dummy_linear_states_v2_parts", joinColumns = [(JoinColumn(name = "output_index", referencedColumnName = "output_index")), (JoinColumn(name = "transaction_id", referencedColumnName = "transaction_id"))])</ID>
<ID>MaxLineLength:DumpHistoryOnErrorInterceptor.kt$DumpHistoryOnErrorInterceptor$val transitionRecord = TransitionDiagnosticRecord(Instant.now(), fiber.id, previousState, nextState, event, transition, continuation)</ID> <ID>MaxLineLength:DumpHistoryOnErrorInterceptor.kt$DumpHistoryOnErrorInterceptor$val transitionRecord = TransitionDiagnosticRecord(Instant.now(), fiber.id, previousState, nextState, event, transition, continuation)</ID>
<ID>MaxLineLength:DurationSerializer.kt$DurationSerializer : Proxy</ID>
<ID>MaxLineLength:E2ETestKeyManagementService.kt$E2ETestKeyManagementService : SingletonSerializeAsTokenKeyManagementServiceInternal</ID> <ID>MaxLineLength:E2ETestKeyManagementService.kt$E2ETestKeyManagementService : SingletonSerializeAsTokenKeyManagementServiceInternal</ID>
<ID>MaxLineLength:EdDSATests.kt$EdDSATests$assertNotEquals(testVectorEd25519ctx.signatureOutputHex, doSign(privateKey, testVectorEd25519ctx.messageToSignHex.hexToByteArray()).toHex().toLowerCase())</ID> <ID>MaxLineLength:EdDSATests.kt$EdDSATests$assertNotEquals(testVectorEd25519ctx.signatureOutputHex, doSign(privateKey, testVectorEd25519ctx.messageToSignHex.hexToByteArray()).toHex().toLowerCase())</ID>
<ID>MaxLineLength:EnumEvolutionSerializer.kt$EnumEvolutionSerializer$val converted = conversions[enumName] ?: throw AMQPNotSerializableException(type, "No rule to evolve enum constant $type::$enumName")</ID> <ID>MaxLineLength:EnumEvolutionSerializer.kt$EnumEvolutionSerializer$val converted = conversions[enumName] ?: throw AMQPNotSerializableException(type, "No rule to evolve enum constant $type::$enumName")</ID>
<ID>MaxLineLength:EnumEvolvabilityTests.kt$EnumEvolvabilityTests$assertThatThrownBy { SerializationOutput(sf).serialize(C(RejectMultipleRenameFrom.A)) }.isInstanceOf(NotSerializableException::class.java) .hasToString("Unable to serialize/deserialize net.corda.serialization.internal.amqp.EnumEvolvabilityTests\$RejectMultipleRenameFrom: " + "There are multiple transformations from D, which is not allowed")</ID> <ID>MaxLineLength:EnumEvolvabilityTests.kt$EnumEvolvabilityTests$assertThatThrownBy { SerializationOutput(sf).serialize(C(RejectMultipleRenameFrom.A)) }.isInstanceOf(NotSerializableException::class.java) .hasToString("Unable to serialize/deserialize net.corda.serialization.internal.amqp.EnumEvolvabilityTests\$RejectMultipleRenameFrom: " + "There are multiple transformations from D, which is not allowed")</ID>
<ID>MaxLineLength:EnumSetSerializer.kt$EnumSetSerializer : Proxy</ID>
<ID>MaxLineLength:ErrorCodeLoggingTests.kt$ErrorCodeLoggingTests$val linesWithErrorCode = logFile.useLines { lines -&gt; lines.filter { line -&gt; line.contains("[errorCode=") }.filter { line -&gt; line.contains("moreInformationAt=https://errors.corda.net/") }.toList() }</ID> <ID>MaxLineLength:ErrorCodeLoggingTests.kt$ErrorCodeLoggingTests$val linesWithErrorCode = logFile.useLines { lines -&gt; lines.filter { line -&gt; line.contains("[errorCode=") }.filter { line -&gt; line.contains("moreInformationAt=https://errors.corda.net/") }.toList() }</ID>
<ID>MaxLineLength:ErrorCodeLoggingTests.kt$fun NodeHandle.logFile(): File</ID> <ID>MaxLineLength:ErrorCodeLoggingTests.kt$fun NodeHandle.logFile(): File</ID>
<ID>MaxLineLength:ErrorFlowTransition.kt$ErrorFlowTransition$val (initiatedSessions, newSessions) = bufferErrorMessagesInInitiatingSessions(startingState.checkpoint.sessions, errorMessages)</ID> <ID>MaxLineLength:ErrorFlowTransition.kt$ErrorFlowTransition$val (initiatedSessions, newSessions) = bufferErrorMessagesInInitiatingSessions(startingState.checkpoint.sessions, errorMessages)</ID>
@ -2159,11 +2142,6 @@
<ID>MaxLineLength:FlowFrameworkPersistenceTests.kt$FlowFrameworkPersistenceTests$assertEquals(payload + 1, secondFlow.getOrThrow().receivedPayload2, "Received payload does not match the expected second value on Node 2")</ID> <ID>MaxLineLength:FlowFrameworkPersistenceTests.kt$FlowFrameworkPersistenceTests$assertEquals(payload + 1, secondFlow.getOrThrow().receivedPayload2, "Received payload does not match the expected second value on Node 2")</ID>
<ID>MaxLineLength:FlowFrameworkPersistenceTests.kt$FlowFrameworkPersistenceTests$assertEquals(payload, secondFlow.getOrThrow().receivedPayload, "Received payload does not match the (restarted) first value on Node 2")</ID> <ID>MaxLineLength:FlowFrameworkPersistenceTests.kt$FlowFrameworkPersistenceTests$assertEquals(payload, secondFlow.getOrThrow().receivedPayload, "Received payload does not match the (restarted) first value on Node 2")</ID>
<ID>MaxLineLength:FlowFrameworkTests.kt$FlowFrameworkTests$assertThat(receivedSessionMessages).hasSize(1)</ID> <ID>MaxLineLength:FlowFrameworkTests.kt$FlowFrameworkTests$assertThat(receivedSessionMessages).hasSize(1)</ID>
<ID>MaxLineLength:FlowFrameworkTests.kt$PingPongFlow$@InitiatingFlow internal</ID>
<ID>MaxLineLength:FlowFrameworkTests.kt$SendAndReceiveFlow$@InitiatingFlow internal</ID>
<ID>MaxLineLength:FlowFrameworkTests.kt$internal fun errorMessage(errorResponse: FlowException? = null)</ID>
<ID>MaxLineLength:FlowFrameworkTests.kt$internal fun sessionConfirm(flowVersion: Int = 1)</ID>
<ID>MaxLineLength:FlowFrameworkTests.kt$internal infix fun Pair&lt;Int, SessionMessage&gt;.to(node: TestStartedNode): SessionTransfer</ID>
<ID>MaxLineLength:FlowLogic.kt$FlowLogic$ @Suspendable @JvmOverloads open fun &lt;R : Any&gt; receiveAll(receiveType: Class&lt;R&gt;, sessions: List&lt;FlowSession&gt;, maySkipCheckpoint: Boolean = false): List&lt;UntrustworthyData&lt;R&gt;&gt;</ID> <ID>MaxLineLength:FlowLogic.kt$FlowLogic$ @Suspendable @JvmOverloads open fun &lt;R : Any&gt; receiveAll(receiveType: Class&lt;R&gt;, sessions: List&lt;FlowSession&gt;, maySkipCheckpoint: Boolean = false): List&lt;UntrustworthyData&lt;R&gt;&gt;</ID>
<ID>MaxLineLength:FlowLogic.kt$FlowLogic$ @Suspendable @JvmOverloads open fun receiveAllMap(sessions: Map&lt;FlowSession, Class&lt;out Any&gt;&gt;, maySkipCheckpoint: Boolean = false): Map&lt;FlowSession, UntrustworthyData&lt;Any&gt;&gt;</ID> <ID>MaxLineLength:FlowLogic.kt$FlowLogic$ @Suspendable @JvmOverloads open fun receiveAllMap(sessions: Map&lt;FlowSession, Class&lt;out Any&gt;&gt;, maySkipCheckpoint: Boolean = false): Map&lt;FlowSession, UntrustworthyData&lt;Any&gt;&gt;</ID>
<ID>MaxLineLength:FlowLogic.kt$FlowLogic$?:</ID> <ID>MaxLineLength:FlowLogic.kt$FlowLogic$?:</ID>
@ -2371,7 +2349,6 @@
<ID>MaxLineLength:InstallShellExtensionsParser.kt$InstallShellExtensionsParser : CliWrapperBase</ID> <ID>MaxLineLength:InstallShellExtensionsParser.kt$InstallShellExtensionsParser : CliWrapperBase</ID>
<ID>MaxLineLength:InstallShellExtensionsParser.kt$ShellExtensionsGenerator$printWarning("Cannot install shell extension for bash major version earlier than $minSupportedBashVersion. Please upgrade your bash version. Aliases should still work.")</ID> <ID>MaxLineLength:InstallShellExtensionsParser.kt$ShellExtensionsGenerator$printWarning("Cannot install shell extension for bash major version earlier than $minSupportedBashVersion. Please upgrade your bash version. Aliases should still work.")</ID>
<ID>MaxLineLength:InstallShellExtensionsParser.kt$ShellExtensionsGenerator$println("Installation complete, ${parent.alias} is available in bash, but autocompletion was not installed because of an old version of bash.")</ID> <ID>MaxLineLength:InstallShellExtensionsParser.kt$ShellExtensionsGenerator$println("Installation complete, ${parent.alias} is available in bash, but autocompletion was not installed because of an old version of bash.")</ID>
<ID>MaxLineLength:InstantSerializer.kt$InstantSerializer : Proxy</ID>
<ID>MaxLineLength:InteractiveShellIntegrationTest.kt$InteractiveShellIntegrationTest$private</ID> <ID>MaxLineLength:InteractiveShellIntegrationTest.kt$InteractiveShellIntegrationTest$private</ID>
<ID>MaxLineLength:InterestSwapRestAPI.kt$InterestRateSwapAPI</ID> <ID>MaxLineLength:InterestSwapRestAPI.kt$InterestRateSwapAPI</ID>
<ID>MaxLineLength:InternalAccessTestHelpers.kt$fun &lt;T&gt; ifThrowsAppend(strToAppendFn: () -&gt; String, block: () -&gt; T): T</ID> <ID>MaxLineLength:InternalAccessTestHelpers.kt$fun &lt;T&gt; ifThrowsAppend(strToAppendFn: () -&gt; String, block: () -&gt; T): T</ID>
@ -2469,9 +2446,6 @@
<ID>MaxLineLength:ListsSerializationTest.kt$ListsSerializationTest.Companion$val envelope = DeserializationInput(SerializerFactoryBuilder.build(context.whitelist, context.deserializationClassLoader)).getEnvelope(serBytes, context)</ID> <ID>MaxLineLength:ListsSerializationTest.kt$ListsSerializationTest.Companion$val envelope = DeserializationInput(SerializerFactoryBuilder.build(context.whitelist, context.deserializationClassLoader)).getEnvelope(serBytes, context)</ID>
<ID>MaxLineLength:ListsSerializationTest.kt$internal inline</ID> <ID>MaxLineLength:ListsSerializationTest.kt$internal inline</ID>
<ID>MaxLineLength:LoadTestConfiguration.kt$RemoteNode</ID> <ID>MaxLineLength:LoadTestConfiguration.kt$RemoteNode</ID>
<ID>MaxLineLength:LocalDateSerializer.kt$LocalDateSerializer : Proxy</ID>
<ID>MaxLineLength:LocalDateTimeSerializer.kt$LocalDateTimeSerializer : Proxy</ID>
<ID>MaxLineLength:LocalDateTimeSerializer.kt$LocalDateTimeSerializer$override val additionalSerializers: Iterable&lt;CustomSerializer&lt;out Any&gt;&gt; = listOf(LocalDateSerializer(factory), LocalTimeSerializer(factory))</ID>
<ID>MaxLineLength:LocalPropertyInformation.kt$LocalPropertyInformation.CalculatedProperty$data</ID> <ID>MaxLineLength:LocalPropertyInformation.kt$LocalPropertyInformation.CalculatedProperty$data</ID>
<ID>MaxLineLength:LocalPropertyInformation.kt$LocalPropertyInformation.ConstructorPairedProperty$data</ID> <ID>MaxLineLength:LocalPropertyInformation.kt$LocalPropertyInformation.ConstructorPairedProperty$data</ID>
<ID>MaxLineLength:LocalPropertyInformation.kt$LocalPropertyInformation.GetterSetterProperty$data</ID> <ID>MaxLineLength:LocalPropertyInformation.kt$LocalPropertyInformation.GetterSetterProperty$data</ID>
@ -2481,9 +2455,6 @@
<ID>MaxLineLength:LocalSerializerFactory.kt$DefaultLocalSerializerFactory$is LocalTypeInformation.AMap</ID> <ID>MaxLineLength:LocalSerializerFactory.kt$DefaultLocalSerializerFactory$is LocalTypeInformation.AMap</ID>
<ID>MaxLineLength:LocalSerializerFactory.kt$DefaultLocalSerializerFactory$private</ID> <ID>MaxLineLength:LocalSerializerFactory.kt$DefaultLocalSerializerFactory$private</ID>
<ID>MaxLineLength:LocalSerializerFactory.kt$DefaultLocalSerializerFactory$private val serializersByActualAndDeclaredType: MutableMap&lt;ActualAndDeclaredType, AMQPSerializer&lt;Any&gt;&gt; = DefaultCacheProvider.createCache()</ID> <ID>MaxLineLength:LocalSerializerFactory.kt$DefaultLocalSerializerFactory$private val serializersByActualAndDeclaredType: MutableMap&lt;ActualAndDeclaredType, AMQPSerializer&lt;Any&gt;&gt; = DefaultCacheProvider.createCache()</ID>
<ID>MaxLineLength:LocalTimeSerializer.kt$LocalTimeSerializer : Proxy</ID>
<ID>MaxLineLength:LocalTimeSerializer.kt$LocalTimeSerializer$override fun fromProxy(proxy: LocalTimeProxy): LocalTime</ID>
<ID>MaxLineLength:LocalTimeSerializer.kt$LocalTimeSerializer$override fun toProxy(obj: LocalTime): LocalTimeProxy</ID>
<ID>MaxLineLength:LocalTypeInformation.kt$LocalTypeInformation.ACollection$data</ID> <ID>MaxLineLength:LocalTypeInformation.kt$LocalTypeInformation.ACollection$data</ID>
<ID>MaxLineLength:LocalTypeInformation.kt$LocalTypeInformation.AnArray$data</ID> <ID>MaxLineLength:LocalTypeInformation.kt$LocalTypeInformation.AnArray$data</ID>
<ID>MaxLineLength:LocalTypeInformation.kt$LocalTypeInformation.Singleton$data</ID> <ID>MaxLineLength:LocalTypeInformation.kt$LocalTypeInformation.Singleton$data</ID>
@ -2763,7 +2734,7 @@
<ID>MaxLineLength:NodeKeystoreCheckTest.kt$NodeKeystoreCheckTest$setPrivateKey(X509Utilities.CORDA_CLIENT_CA, nodeCA.keyPair.private, listOf(badNodeCACert, badRoot), signingCertStore.entryPassword)</ID> <ID>MaxLineLength:NodeKeystoreCheckTest.kt$NodeKeystoreCheckTest$setPrivateKey(X509Utilities.CORDA_CLIENT_CA, nodeCA.keyPair.private, listOf(badNodeCACert, badRoot), signingCertStore.entryPassword)</ID>
<ID>MaxLineLength:NodeKeystoreCheckTest.kt$NodeKeystoreCheckTest$val badNodeCACert = X509Utilities.createCertificate(CertificateType.NODE_CA, badRoot, badRootKeyPair, ALICE_NAME.x500Principal, nodeCA.keyPair.public)</ID> <ID>MaxLineLength:NodeKeystoreCheckTest.kt$NodeKeystoreCheckTest$val badNodeCACert = X509Utilities.createCertificate(CertificateType.NODE_CA, badRoot, badRootKeyPair, ALICE_NAME.x500Principal, nodeCA.keyPair.public)</ID>
<ID>MaxLineLength:NodeKeystoreCheckTest.kt$NodeKeystoreCheckTest$val p2pSslConfig = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory, keyStorePassword = keystorePassword, trustStorePassword = keystorePassword)</ID> <ID>MaxLineLength:NodeKeystoreCheckTest.kt$NodeKeystoreCheckTest$val p2pSslConfig = CertificateStoreStubs.P2P.withCertificatesDirectory(certificatesDirectory, keyStorePassword = keystorePassword, trustStorePassword = keystorePassword)</ID>
<ID>MaxLineLength:NodeMonitorModel.kt$NodeMonitorModel${ rpc = CordaRPCClient(nodeHostAndPort).start(username, password) proxyObservable.value = rpc.proxy // Vault snapshot (force single page load with MAX_PAGE_SIZE) + updates val ( statesSnapshot, vaultUpdates ) = rpc.proxy.vaultTrackBy&lt;ContractState&gt;( QueryCriteria.VaultQueryCriteria(Vault.StateStatus.ALL), PageSpecification(DEFAULT_PAGE_NUM, MAX_PAGE_SIZE) ) val unconsumedStates = statesSnapshot.states.filterIndexed { index, _ -&gt; statesSnapshot.statesMetadata[index].status == Vault.StateStatus.UNCONSUMED }.toSet() val consumedStates = statesSnapshot.states.toSet() - unconsumedStates val initialVaultUpdate = Vault.Update(consumedStates, unconsumedStates, references = emptySet()) vaultUpdates.startWith(initialVaultUpdate).subscribe(vaultUpdatesSubject::onNext) // Transactions val (transactions, newTransactions) = @Suppress("DEPRECATION") rpc.proxy.internalVerifiedTransactionsFeed() newTransactions.startWith(transactions).subscribe(transactionsSubject::onNext) // SM -&gt; TX mapping val (smTxMappings, futureSmTxMappings) = rpc.proxy.stateMachineRecordedTransactionMappingFeed() futureSmTxMappings.startWith(smTxMappings).subscribe(stateMachineTransactionMappingSubject::onNext) // Parties on network val (parties, futurePartyUpdate) = rpc.proxy.networkMapFeed() futurePartyUpdate.startWith(parties.map(MapChange::Added)).subscribe(networkMapSubject::onNext) val stateMachines = rpc.proxy.stateMachinesSnapshot() notaryIdentities = rpc.proxy.notaryIdentities() // Extract the flow tracking stream // TODO is there a nicer way of doing this? Stream of streams in general results in code like this... // TODO `progressTrackingSubject` doesn't seem to be used anymore - should it be removed? val currentProgressTrackerUpdates = stateMachines.mapNotNull { stateMachine -&gt; ProgressTrackingEvent.createStreamFromStateMachineInfo(stateMachine) } val futureProgressTrackerUpdates = stateMachineUpdatesSubject.map { stateMachineUpdate -&gt; if (stateMachineUpdate is StateMachineUpdate.Added) { ProgressTrackingEvent.createStreamFromStateMachineInfo(stateMachineUpdate.stateMachineInfo) ?: Observable.empty&lt;ProgressTrackingEvent&gt;() } else { Observable.empty&lt;ProgressTrackingEvent&gt;() } } // We need to retry, because when flow errors, we unsubscribe from progressTrackingSubject. So we end up with stream of state machine updates and no progress trackers. futureProgressTrackerUpdates.startWith(currentProgressTrackerUpdates).flatMap { it }.retry().subscribe(progressTrackingSubject) }</ID> <ID>MaxLineLength:NodeMonitorModel.kt$NodeMonitorModel${ rpc = CordaRPCClient(nodeHostAndPort).start(username, password, GracefulReconnect()) proxyObservable.value = rpc.proxy // Vault snapshot (force single page load with MAX_PAGE_SIZE) + updates val ( statesSnapshot, vaultUpdates ) = rpc.proxy.vaultTrackBy&lt;ContractState&gt;( QueryCriteria.VaultQueryCriteria(Vault.StateStatus.ALL), PageSpecification(DEFAULT_PAGE_NUM, MAX_PAGE_SIZE) ) val unconsumedStates = statesSnapshot.states.filterIndexed { index, _ -&gt; statesSnapshot.statesMetadata[index].status == Vault.StateStatus.UNCONSUMED }.toSet() val consumedStates = statesSnapshot.states.toSet() - unconsumedStates val initialVaultUpdate = Vault.Update(consumedStates, unconsumedStates, references = emptySet()) vaultUpdates.startWith(initialVaultUpdate).subscribe(vaultUpdatesSubject::onNext) // Transactions val (transactions, newTransactions) = @Suppress("DEPRECATION") rpc.proxy.internalVerifiedTransactionsFeed() newTransactions.startWith(transactions).subscribe(transactionsSubject::onNext) // SM -&gt; TX mapping val (smTxMappings, futureSmTxMappings) = rpc.proxy.stateMachineRecordedTransactionMappingFeed() futureSmTxMappings.startWith(smTxMappings).subscribe(stateMachineTransactionMappingSubject::onNext) // Parties on network val (parties, futurePartyUpdate) = rpc.proxy.networkMapFeed() futurePartyUpdate.startWith(parties.map(MapChange::Added)).subscribe(networkMapSubject::onNext) val stateMachines = rpc.proxy.stateMachinesSnapshot() notaryIdentities = rpc.proxy.notaryIdentities() // Extract the flow tracking stream // TODO is there a nicer way of doing this? Stream of streams in general results in code like this... // TODO `progressTrackingSubject` doesn't seem to be used anymore - should it be removed? val currentProgressTrackerUpdates = stateMachines.mapNotNull { stateMachine -&gt; ProgressTrackingEvent.createStreamFromStateMachineInfo(stateMachine) } val futureProgressTrackerUpdates = stateMachineUpdatesSubject.map { stateMachineUpdate -&gt; if (stateMachineUpdate is StateMachineUpdate.Added) { ProgressTrackingEvent.createStreamFromStateMachineInfo(stateMachineUpdate.stateMachineInfo) ?: Observable.empty&lt;ProgressTrackingEvent&gt;() } else { Observable.empty&lt;ProgressTrackingEvent&gt;() } } // We need to retry, because when flow errors, we unsubscribe from progressTrackingSubject. So we end up with stream of state machine updates and no progress trackers. futureProgressTrackerUpdates.startWith(currentProgressTrackerUpdates).flatMap { it }.retry().subscribe(progressTrackingSubject) }</ID>
<ID>MaxLineLength:NodeNamedCache.kt$DefaultNamedCacheFactory$name.startsWith("RPCSecurityManagerShiroCache_") -&gt; with(security?.authService?.options?.cache!!) { caffeine.maximumSize(maxEntries).expireAfterWrite(expireAfterSecs, TimeUnit.SECONDS) }</ID> <ID>MaxLineLength:NodeNamedCache.kt$DefaultNamedCacheFactory$name.startsWith("RPCSecurityManagerShiroCache_") -&gt; with(security?.authService?.options?.cache!!) { caffeine.maximumSize(maxEntries).expireAfterWrite(expireAfterSecs, TimeUnit.SECONDS) }</ID>
<ID>MaxLineLength:NodeNamedCache.kt$DefaultNamedCacheFactory$open</ID> <ID>MaxLineLength:NodeNamedCache.kt$DefaultNamedCacheFactory$open</ID>
<ID>MaxLineLength:NodeNamedCache.kt$DefaultNamedCacheFactory$override fun bindWithConfig(nodeConfiguration: NodeConfiguration): BindableNamedCacheFactory</ID> <ID>MaxLineLength:NodeNamedCache.kt$DefaultNamedCacheFactory$override fun bindWithConfig(nodeConfiguration: NodeConfiguration): BindableNamedCacheFactory</ID>
@ -2970,15 +2941,10 @@
<ID>MaxLineLength:ObservableUtilities.kt$ @Suppress("UNCHECKED_CAST") fun &lt;K : Any, A : Any, B&gt; ObservableList&lt;out A&gt;.associateByAggregation(toKey: (A) -&gt; K, assemble: (K, A) -&gt; B): ObservableMap&lt;K, ObservableList&lt;B&gt;&gt;</ID> <ID>MaxLineLength:ObservableUtilities.kt$ @Suppress("UNCHECKED_CAST") fun &lt;K : Any, A : Any, B&gt; ObservableList&lt;out A&gt;.associateByAggregation(toKey: (A) -&gt; K, assemble: (K, A) -&gt; B): ObservableMap&lt;K, ObservableList&lt;B&gt;&gt;</ID>
<ID>MaxLineLength:ObservableUtilities.kt${ //TODO This is a tactical work round for an issue with SAM conversion (https://youtrack.jetbrains.com/issue/ALL-1552) so that the M10 explorer works. return uncheckedCast(uncheckedCast&lt;Any, ObservableList&lt;A?&gt;&gt;(this).filtered { t -&gt; t != null }) }</ID> <ID>MaxLineLength:ObservableUtilities.kt${ //TODO This is a tactical work round for an issue with SAM conversion (https://youtrack.jetbrains.com/issue/ALL-1552) so that the M10 explorer works. return uncheckedCast(uncheckedCast&lt;Any, ObservableList&lt;A?&gt;&gt;(this).filtered { t -&gt; t != null }) }</ID>
<ID>MaxLineLength:ObserverNodeTransactionTests.kt$ObserverNodeTransactionTests.StartMessageChainFlow$val txBuilder = TransactionBuilder(notary).withItems(StateAndContract(messageState, MESSAGE_CHAIN_CONTRACT_PROGRAM_ID), txCommand)</ID> <ID>MaxLineLength:ObserverNodeTransactionTests.kt$ObserverNodeTransactionTests.StartMessageChainFlow$val txBuilder = TransactionBuilder(notary).withItems(StateAndContract(messageState, MESSAGE_CHAIN_CONTRACT_PROGRAM_ID), txCommand)</ID>
<ID>MaxLineLength:OffsetDateTimeSerializer.kt$OffsetDateTimeSerializer : Proxy</ID>
<ID>MaxLineLength:OffsetDateTimeSerializer.kt$OffsetDateTimeSerializer$override val additionalSerializers: Iterable&lt;CustomSerializer&lt;out Any&gt;&gt; = listOf(LocalDateTimeSerializer(factory), ZoneIdSerializer(factory))</ID>
<ID>MaxLineLength:OffsetTimeSerializer.kt$OffsetTimeSerializer : Proxy</ID>
<ID>MaxLineLength:OffsetTimeSerializer.kt$OffsetTimeSerializer$override val additionalSerializers: Iterable&lt;CustomSerializer&lt;out Any&gt;&gt; = listOf(LocalTimeSerializer(factory), ZoneIdSerializer(factory))</ID>
<ID>MaxLineLength:OnLedgerAsset.kt$OnLedgerAsset.Companion$deriveState: (TransactionState&lt;S&gt;, Amount&lt;Issued&lt;T&gt;&gt;, AbstractParty) -&gt; TransactionState&lt;S&gt;</ID> <ID>MaxLineLength:OnLedgerAsset.kt$OnLedgerAsset.Companion$deriveState: (TransactionState&lt;S&gt;, Amount&lt;Issued&lt;T&gt;&gt;, AbstractParty) -&gt; TransactionState&lt;S&gt;</ID>
<ID>MaxLineLength:OnLedgerAsset.kt$OnLedgerAsset.Companion$generateMoveCommand: () -&gt; CommandData</ID> <ID>MaxLineLength:OnLedgerAsset.kt$OnLedgerAsset.Companion$generateMoveCommand: () -&gt; CommandData</ID>
<ID>MaxLineLength:OpenGammaCordaUtils.kt$ fun InitialMarginTriple.toCordaCompatible()</ID> <ID>MaxLineLength:OpenGammaCordaUtils.kt$ fun InitialMarginTriple.toCordaCompatible()</ID>
<ID>MaxLineLength:OpenGammaCordaUtils.kt$return MultiCurrencyAmount.of(this.amounts.map { CurrencyAmount.of(Currency.of(it.currency.code).serialize().deserialize(), twoDecimalPlaces((it.amount))) })</ID> <ID>MaxLineLength:OpenGammaCordaUtils.kt$return MultiCurrencyAmount.of(this.amounts.map { CurrencyAmount.of(Currency.of(it.currency.code).serialize().deserialize(), twoDecimalPlaces((it.amount))) })</ID>
<ID>MaxLineLength:OptionalSerializer.kt$OptionalSerializer : Proxy</ID>
<ID>MaxLineLength:OracleNodeTearOffTests.kt$OracleNodeTearOffTests$TransactionBuilder(DUMMY_NOTARY) .withItems(TransactionState(1000.DOLLARS.CASH issuedBy dummyCashIssuer.party ownedBy alice.party, Cash.PROGRAM_ID, DUMMY_NOTARY))</ID> <ID>MaxLineLength:OracleNodeTearOffTests.kt$OracleNodeTearOffTests$TransactionBuilder(DUMMY_NOTARY) .withItems(TransactionState(1000.DOLLARS.CASH issuedBy dummyCashIssuer.party ownedBy alice.party, Cash.PROGRAM_ID, DUMMY_NOTARY))</ID>
<ID>MaxLineLength:P2PFlowsDrainingModeTest.kt$P2PFlowsDrainingModeTest$nodeA.rpc.hasCancelledDrainingShutdown().doOnError(Throwable::printStackTrace).doOnError { successful = false }.doOnCompleted { successful = true }.doAfterTerminate(latch::countDown).subscribe()</ID> <ID>MaxLineLength:P2PFlowsDrainingModeTest.kt$P2PFlowsDrainingModeTest$nodeA.rpc.hasCancelledDrainingShutdown().doOnError(Throwable::printStackTrace).doOnError { successful = false }.doOnCompleted { successful = true }.doAfterTerminate(latch::countDown).subscribe()</ID>
<ID>MaxLineLength:P2PFlowsDrainingModeTest.kt$P2PFlowsDrainingModeTest$nodeA.rpc.waitForShutdown().doOnError(Throwable::printStackTrace).doOnError { successful = false }.doOnCompleted(nodeA::stop)</ID> <ID>MaxLineLength:P2PFlowsDrainingModeTest.kt$P2PFlowsDrainingModeTest$nodeA.rpc.waitForShutdown().doOnError(Throwable::printStackTrace).doOnError { successful = false }.doOnCompleted(nodeA::stop)</ID>
@ -3008,7 +2974,6 @@
<ID>MaxLineLength:Perceivable.kt$fun interest(@Suppress("UNUSED_PARAMETER") amount: BigDecimal, @Suppress("UNUSED_PARAMETER") dayCountConvention: String, @Suppress("UNUSED_PARAMETER") interest: BigDecimal /* todo - appropriate type */, @Suppress("UNUSED_PARAMETER") start: String, @Suppress("UNUSED_PARAMETER") end: String): Perceivable&lt;BigDecimal&gt;</ID> <ID>MaxLineLength:Perceivable.kt$fun interest(@Suppress("UNUSED_PARAMETER") amount: BigDecimal, @Suppress("UNUSED_PARAMETER") dayCountConvention: String, @Suppress("UNUSED_PARAMETER") interest: BigDecimal /* todo - appropriate type */, @Suppress("UNUSED_PARAMETER") start: String, @Suppress("UNUSED_PARAMETER") end: String): Perceivable&lt;BigDecimal&gt;</ID>
<ID>MaxLineLength:Perceivable.kt$fun interest(@Suppress("UNUSED_PARAMETER") amount: BigDecimal, @Suppress("UNUSED_PARAMETER") dayCountConvention: String, @Suppress("UNUSED_PARAMETER") interest: Perceivable&lt;BigDecimal&gt; /* todo - appropriate type */, @Suppress("UNUSED_PARAMETER") start: Perceivable&lt;Instant&gt;, @Suppress("UNUSED_PARAMETER") end: Perceivable&lt;Instant&gt;): Perceivable&lt;BigDecimal&gt;</ID> <ID>MaxLineLength:Perceivable.kt$fun interest(@Suppress("UNUSED_PARAMETER") amount: BigDecimal, @Suppress("UNUSED_PARAMETER") dayCountConvention: String, @Suppress("UNUSED_PARAMETER") interest: Perceivable&lt;BigDecimal&gt; /* todo - appropriate type */, @Suppress("UNUSED_PARAMETER") start: Perceivable&lt;Instant&gt;, @Suppress("UNUSED_PARAMETER") end: Perceivable&lt;Instant&gt;): Perceivable&lt;BigDecimal&gt;</ID>
<ID>MaxLineLength:Perceivable.kt$fun interest(@Suppress("UNUSED_PARAMETER") amount: BigDecimal, @Suppress("UNUSED_PARAMETER") dayCountConvention: String, @Suppress("UNUSED_PARAMETER") interest: Perceivable&lt;BigDecimal&gt; /* todo - appropriate type */, @Suppress("UNUSED_PARAMETER") start: String, @Suppress("UNUSED_PARAMETER") end: String): Perceivable&lt;BigDecimal&gt;</ID> <ID>MaxLineLength:Perceivable.kt$fun interest(@Suppress("UNUSED_PARAMETER") amount: BigDecimal, @Suppress("UNUSED_PARAMETER") dayCountConvention: String, @Suppress("UNUSED_PARAMETER") interest: Perceivable&lt;BigDecimal&gt; /* todo - appropriate type */, @Suppress("UNUSED_PARAMETER") start: String, @Suppress("UNUSED_PARAMETER") end: String): Perceivable&lt;BigDecimal&gt;</ID>
<ID>MaxLineLength:PeriodSerializer.kt$PeriodSerializer : Proxy</ID>
<ID>MaxLineLength:PersistentIdentityMigrationNewTable.kt$PersistentIdentityMigrationNewTable : CordaMigration</ID> <ID>MaxLineLength:PersistentIdentityMigrationNewTable.kt$PersistentIdentityMigrationNewTable : CordaMigration</ID>
<ID>MaxLineLength:PersistentIdentityMigrationNewTable.kt$PersistentIdentityMigrationNewTable$throw PersistentIdentitiesMigrationException("Cannot migrate persistent identities as liquibase failed to provide a suitable database connection")</ID> <ID>MaxLineLength:PersistentIdentityMigrationNewTable.kt$PersistentIdentityMigrationNewTable$throw PersistentIdentitiesMigrationException("Cannot migrate persistent identities as liquibase failed to provide a suitable database connection")</ID>
<ID>MaxLineLength:PersistentIdentityMigrationNewTableTest.kt$PersistentIdentityMigrationNewTableTest$session.save(PersistentIdentityService.PersistentPublicKeyHashToCertificate(it.owningKey.hash.toString(), it.certPath.encoded))</ID> <ID>MaxLineLength:PersistentIdentityMigrationNewTableTest.kt$PersistentIdentityMigrationNewTableTest$session.save(PersistentIdentityService.PersistentPublicKeyHashToCertificate(it.owningKey.hash.toString(), it.certPath.encoded))</ID>
@ -3044,7 +3009,6 @@
<ID>MaxLineLength:PortfolioApiUtils.kt$PortfolioApiUtils$val yieldCurveCurrenciesValues = marketData.filter { !it.key.contains("/") }.map { it -&gt; Triple(it.key.split("-")[0], it.key.split("-", limit = 2)[1], it.value) }</ID> <ID>MaxLineLength:PortfolioApiUtils.kt$PortfolioApiUtils$val yieldCurveCurrenciesValues = marketData.filter { !it.key.contains("/") }.map { it -&gt; Triple(it.key.split("-")[0], it.key.split("-", limit = 2)[1], it.value) }</ID>
<ID>MaxLineLength:PortfolioState.kt$PortfolioState$return TransactionBuilder(notary).withItems(StateAndContract(copy(), PORTFOLIO_SWAP_PROGRAM_ID), Command(PortfolioSwap.Commands.Agree(), participants.map { it.owningKey }))</ID> <ID>MaxLineLength:PortfolioState.kt$PortfolioState$return TransactionBuilder(notary).withItems(StateAndContract(copy(), PORTFOLIO_SWAP_PROGRAM_ID), Command(PortfolioSwap.Commands.Agree(), participants.map { it.owningKey }))</ID>
<ID>MaxLineLength:PrintingInterceptor.kt$PrintingInterceptor$val transitionRecord = TransitionDiagnosticRecord(Instant.now(), fiber.id, previousState, nextState, event, transition, continuation)</ID> <ID>MaxLineLength:PrintingInterceptor.kt$PrintingInterceptor$val transitionRecord = TransitionDiagnosticRecord(Instant.now(), fiber.id, previousState, nextState, event, transition, continuation)</ID>
<ID>MaxLineLength:PrivateKeySerializer.kt$PrivateKeySerializer$override val schemaForDocumentation = Schema(listOf(RestrictedType(type.toString(), "", listOf(type.toString()), AMQPTypeIdentifiers.primitiveTypeName(ByteArray::class.java), descriptor, emptyList())))</ID>
<ID>MaxLineLength:ProgressTracker.kt$ProgressTracker$log.warnOnce("Found ProgressTracker Step(s) with the same label: ${labels.groupBy { it }.filter { it.value.size &gt; 1 }.map { it.key }}")</ID> <ID>MaxLineLength:ProgressTracker.kt$ProgressTracker$log.warnOnce("Found ProgressTracker Step(s) with the same label: ${labels.groupBy { it }.filter { it.value.size &gt; 1 }.map { it.key }}")</ID>
<ID>MaxLineLength:ProgressTracker.kt$ProgressTracker.Step$private fun definitionLocation(): String</ID> <ID>MaxLineLength:ProgressTracker.kt$ProgressTracker.Step$private fun definitionLocation(): String</ID>
<ID>MaxLineLength:Properties.kt$DelegatedProperty$private abstract</ID> <ID>MaxLineLength:Properties.kt$DelegatedProperty$private abstract</ID>
@ -3077,7 +3041,6 @@
<ID>MaxLineLength:PropertyTest.kt$PropertyTest$val property = Configuration.Property.Definition.long(key).map(::AtomicLong).list().map { list -&gt; list.map(AtomicLong::get).max() }.optional()</ID> <ID>MaxLineLength:PropertyTest.kt$PropertyTest$val property = Configuration.Property.Definition.long(key).map(::AtomicLong).list().map { list -&gt; list.map(AtomicLong::get).max() }.optional()</ID>
<ID>MaxLineLength:PropertyValidationTest.kt$PropertyValidationTest$return invalid(Configuration.Validation.Error.BadValue.of("Value must be of format \"host(String):port(Int &gt; 0)\" e.g., \"127.0.0.1:8080\""))</ID> <ID>MaxLineLength:PropertyValidationTest.kt$PropertyValidationTest$return invalid(Configuration.Validation.Error.BadValue.of("Value must be of format \"host(String):port(Int &gt; 0)\" e.g., \"127.0.0.1:8080\""))</ID>
<ID>MaxLineLength:ProviderMap.kt$// This registration is needed for reading back EdDSA key from java keystore. // TODO: Find a way to make JKS work with bouncy castle provider or implement our own provide so we don't have to register bouncy castle provider. Security.addProvider(it)</ID> <ID>MaxLineLength:ProviderMap.kt$// This registration is needed for reading back EdDSA key from java keystore. // TODO: Find a way to make JKS work with bouncy castle provider or implement our own provide so we don't have to register bouncy castle provider. Security.addProvider(it)</ID>
<ID>MaxLineLength:PublicKeySerializer.kt$PublicKeySerializer$override val schemaForDocumentation = Schema(listOf(RestrictedType(type.toString(), "", listOf(type.toString()), AMQPTypeIdentifiers.primitiveTypeName(ByteArray::class.java), descriptor, emptyList())))</ID>
<ID>MaxLineLength:PublicKeyToOwningIdentityCacheImpl.kt$PublicKeyToOwningIdentityCacheImpl$criteriaBuilder.equal(queryRoot.get&lt;String&gt;(BasicHSMKeyManagementService.PersistentKey::publicKeyHash.name), key.toStringShort())</ID> <ID>MaxLineLength:PublicKeyToOwningIdentityCacheImpl.kt$PublicKeyToOwningIdentityCacheImpl$criteriaBuilder.equal(queryRoot.get&lt;String&gt;(BasicHSMKeyManagementService.PersistentKey::publicKeyHash.name), key.toStringShort())</ID>
<ID>MaxLineLength:PublicKeyToOwningIdentityCacheImpl.kt$PublicKeyToOwningIdentityCacheImpl$criteriaBuilder.equal(queryRoot.get&lt;String&gt;(PersistentIdentityService.PersistentPublicKeyHashToCertificate::publicKeyHash.name), key.toStringShort())</ID> <ID>MaxLineLength:PublicKeyToOwningIdentityCacheImpl.kt$PublicKeyToOwningIdentityCacheImpl$criteriaBuilder.equal(queryRoot.get&lt;String&gt;(PersistentIdentityService.PersistentPublicKeyHashToCertificate::publicKeyHash.name), key.toStringShort())</ID>
<ID>MaxLineLength:PushedNode.kt$PushedNode$return NodeInstanceRequest(configFile, baseDirectory, copiedNodeConfig, copiedNodeDir, nodeConfig, localImageId, remoteImageName, nodeInstanceName, actualX500, expectedFqName)</ID> <ID>MaxLineLength:PushedNode.kt$PushedNode$return NodeInstanceRequest(configFile, baseDirectory, copiedNodeConfig, copiedNodeDir, nodeConfig, localImageId, remoteImageName, nodeInstanceName, actualX500, expectedFqName)</ID>
@ -3144,8 +3107,6 @@
<ID>MaxLineLength:RPCApi.kt$private fun Trace.mapToExternal(message: ClientMessage)</ID> <ID>MaxLineLength:RPCApi.kt$private fun Trace.mapToExternal(message: ClientMessage)</ID>
<ID>MaxLineLength:RPCApi.kt$return invocationId(RPC_ID_FIELD_NAME, RPC_ID_TIMESTAMP_FIELD_NAME) ?: throw IllegalStateException("Cannot extract reply id from client message.")</ID> <ID>MaxLineLength:RPCApi.kt$return invocationId(RPC_ID_FIELD_NAME, RPC_ID_TIMESTAMP_FIELD_NAME) ?: throw IllegalStateException("Cannot extract reply id from client message.")</ID>
<ID>MaxLineLength:RPCApi.kt$return sessionId(RPC_SESSION_ID_FIELD_NAME, RPC_SESSION_ID_TIMESTAMP_FIELD_NAME) ?: throw IllegalStateException("Cannot extract the session id from client message.")</ID> <ID>MaxLineLength:RPCApi.kt$return sessionId(RPC_SESSION_ID_FIELD_NAME, RPC_SESSION_ID_TIMESTAMP_FIELD_NAME) ?: throw IllegalStateException("Cannot extract the session id from client message.")</ID>
<ID>MaxLineLength:RPCClientProxyHandler.kt$RPCClientProxyHandler$private</ID>
<ID>MaxLineLength:RPCClientProxyHandler.kt$RPCClientProxyHandler$private val serializationContextWithObservableContext = RpcClientObservableDeSerializer.createContext(serializationContext, observableContext)</ID>
<ID>MaxLineLength:RPCClientProxyHandler.kt$RPCClientProxyHandler$throw UnsupportedOperationException("Method $calledMethod was added in RPC protocol version $sinceVersion but the server is running $serverProtocolVersion")</ID> <ID>MaxLineLength:RPCClientProxyHandler.kt$RPCClientProxyHandler$throw UnsupportedOperationException("Method $calledMethod was added in RPC protocol version $sinceVersion but the server is running $serverProtocolVersion")</ID>
<ID>MaxLineLength:RPCDriver.kt$RPCDriverDSL$val artemisConfig = createRpcServerArtemisConfig(maxFileSize, maxBufferedBytesPerClient, driverDSL.driverDirectory / serverName, hostAndPort)</ID> <ID>MaxLineLength:RPCDriver.kt$RPCDriverDSL$val artemisConfig = createRpcServerArtemisConfig(maxFileSize, maxBufferedBytesPerClient, driverDSL.driverDirectory / serverName, hostAndPort)</ID>
<ID>MaxLineLength:RPCDriver.kt$RPCDriverDSL.Companion$fun createRpcServerArtemisConfig(maxFileSize: Int, maxBufferedBytesPerClient: Long, baseDirectory: Path, hostAndPort: NetworkHostAndPort): Configuration</ID> <ID>MaxLineLength:RPCDriver.kt$RPCDriverDSL.Companion$fun createRpcServerArtemisConfig(maxFileSize: Int, maxBufferedBytesPerClient: Long, baseDirectory: Path, hostAndPort: NetworkHostAndPort): Configuration</ID>
@ -3285,8 +3246,6 @@
<ID>MaxLineLength:SerializationAPI.kt$SerializationFactory$abstract</ID> <ID>MaxLineLength:SerializationAPI.kt$SerializationFactory$abstract</ID>
<ID>MaxLineLength:SerializationAPI.kt$context: SerializationContext = serializationFactory.defaultContext</ID> <ID>MaxLineLength:SerializationAPI.kt$context: SerializationContext = serializationFactory.defaultContext</ID>
<ID>MaxLineLength:SerializationAPI.kt$inline</ID> <ID>MaxLineLength:SerializationAPI.kt$inline</ID>
<ID>MaxLineLength:SerializationEnvironment.kt$"Expected exactly 1 of {${serializationEnvFields.joinToString(", ") { it.name }}} but got: {${it.joinToString(", ") { it.first }}}"</ID>
<ID>MaxLineLength:SerializationEnvironment.kt$private val serializationEnvFields = listOf(_nodeSerializationEnv, _driverSerializationEnv, _contextSerializationEnv, _inheritableContextSerializationEnv)</ID>
<ID>MaxLineLength:SerializationEnvironment.kt$val _inheritableContextSerializationEnv = InheritableThreadLocalToggleField&lt;SerializationEnvironment&gt;("inheritableContextSerializationEnv") { stack -&gt; stack.fold(false) { isAGlobalThreadBeingCreated, e -&gt; isAGlobalThreadBeingCreated || (e.className == "io.netty.util.concurrent.GlobalEventExecutor" &amp;&amp; e.methodName == "startThread") || (e.className == "java.util.concurrent.ForkJoinPool\$DefaultForkJoinWorkerThreadFactory" &amp;&amp; e.methodName == "newThread") } }</ID> <ID>MaxLineLength:SerializationEnvironment.kt$val _inheritableContextSerializationEnv = InheritableThreadLocalToggleField&lt;SerializationEnvironment&gt;("inheritableContextSerializationEnv") { stack -&gt; stack.fold(false) { isAGlobalThreadBeingCreated, e -&gt; isAGlobalThreadBeingCreated || (e.className == "io.netty.util.concurrent.GlobalEventExecutor" &amp;&amp; e.methodName == "startThread") || (e.className == "java.util.concurrent.ForkJoinPool\$DefaultForkJoinWorkerThreadFactory" &amp;&amp; e.methodName == "newThread") } }</ID>
<ID>MaxLineLength:SerializationFactory.kt$SerializationFactory$abstract</ID> <ID>MaxLineLength:SerializationFactory.kt$SerializationFactory$abstract</ID>
<ID>MaxLineLength:SerializationOutputTests.kt$SerializationOutputTests$assertArrayEquals(data, DeserializationInput(factory).deserialize(compressed, testSerializationContext.withEncodingWhitelist(encodingWhitelist)))</ID> <ID>MaxLineLength:SerializationOutputTests.kt$SerializationOutputTests$assertArrayEquals(data, DeserializationInput(factory).deserialize(compressed, testSerializationContext.withEncodingWhitelist(encodingWhitelist)))</ID>
@ -3468,7 +3427,6 @@
<ID>MaxLineLength:ThrowableSerializer.kt$StackTraceElementSerializer : Proxy</ID> <ID>MaxLineLength:ThrowableSerializer.kt$StackTraceElementSerializer : Proxy</ID>
<ID>MaxLineLength:ThrowableSerializer.kt$StackTraceElementSerializer$override fun fromProxy(proxy: StackTraceElementProxy): StackTraceElement</ID> <ID>MaxLineLength:ThrowableSerializer.kt$StackTraceElementSerializer$override fun fromProxy(proxy: StackTraceElementProxy): StackTraceElement</ID>
<ID>MaxLineLength:ThrowableSerializer.kt$StackTraceElementSerializer$override fun toProxy(obj: StackTraceElement): StackTraceElementProxy</ID> <ID>MaxLineLength:ThrowableSerializer.kt$StackTraceElementSerializer$override fun toProxy(obj: StackTraceElement): StackTraceElementProxy</ID>
<ID>MaxLineLength:ThrowableSerializer.kt$ThrowableSerializer : Proxy</ID>
<ID>MaxLineLength:ThrowableSerializer.kt$ThrowableSerializer${ try { // TODO: This will need reworking when we have multiple class loaders val clazz = Class.forName(proxy.exceptionClass, false, factory.classloader) // If it is CordaException or CordaRuntimeException, we can seek any constructor and then set the properties // Otherwise we just make a CordaRuntimeException if (CordaThrowable::class.java.isAssignableFrom(clazz) &amp;&amp; Throwable::class.java.isAssignableFrom(clazz)) { val typeInformation = factory.getTypeInformation(clazz) val constructor = typeInformation.constructor val params = constructor.parameters.map { parameter -&gt; proxy.additionalProperties[parameter.name] ?: proxy.additionalProperties[parameter.name.capitalize()] } val throwable = constructor.observedMethod.newInstance(*params.toTypedArray()) (throwable as CordaThrowable).apply { if (this.javaClass.name != proxy.exceptionClass) this.originalExceptionClassName = proxy.exceptionClass this.setMessage(proxy.message) this.setCause(proxy.cause) this.addSuppressed(proxy.suppressed) } return (throwable as Throwable).apply { this.stackTrace = proxy.stackTrace } } } catch (e: Exception) { logger.warn("Unexpected exception de-serializing throwable: ${proxy.exceptionClass}. Converting to CordaRuntimeException.", e) } // If the criteria are not met or we experience an exception constructing the exception, we fall back to our own unchecked exception. return CordaRuntimeException(proxy.exceptionClass, null, null).apply { this.setMessage(proxy.message) this.setCause(proxy.cause) this.stackTrace = proxy.stackTrace this.addSuppressed(proxy.suppressed) } }</ID> <ID>MaxLineLength:ThrowableSerializer.kt$ThrowableSerializer${ try { // TODO: This will need reworking when we have multiple class loaders val clazz = Class.forName(proxy.exceptionClass, false, factory.classloader) // If it is CordaException or CordaRuntimeException, we can seek any constructor and then set the properties // Otherwise we just make a CordaRuntimeException if (CordaThrowable::class.java.isAssignableFrom(clazz) &amp;&amp; Throwable::class.java.isAssignableFrom(clazz)) { val typeInformation = factory.getTypeInformation(clazz) val constructor = typeInformation.constructor val params = constructor.parameters.map { parameter -&gt; proxy.additionalProperties[parameter.name] ?: proxy.additionalProperties[parameter.name.capitalize()] } val throwable = constructor.observedMethod.newInstance(*params.toTypedArray()) (throwable as CordaThrowable).apply { if (this.javaClass.name != proxy.exceptionClass) this.originalExceptionClassName = proxy.exceptionClass this.setMessage(proxy.message) this.setCause(proxy.cause) this.addSuppressed(proxy.suppressed) } return (throwable as Throwable).apply { this.stackTrace = proxy.stackTrace } } } catch (e: Exception) { logger.warn("Unexpected exception de-serializing throwable: ${proxy.exceptionClass}. Converting to CordaRuntimeException.", e) } // If the criteria are not met or we experience an exception constructing the exception, we fall back to our own unchecked exception. return CordaRuntimeException(proxy.exceptionClass, null, null).apply { this.setMessage(proxy.message) this.setCause(proxy.cause) this.stackTrace = proxy.stackTrace this.addSuppressed(proxy.suppressed) } }</ID>
<ID>MaxLineLength:TimedFlowTests.kt$TimedFlowTests$addOutputState(DummyContract.SingleOwnerState(owner = info.singleIdentity()), DummyContract.PROGRAM_ID, AlwaysAcceptAttachmentConstraint)</ID> <ID>MaxLineLength:TimedFlowTests.kt$TimedFlowTests$addOutputState(DummyContract.SingleOwnerState(owner = info.singleIdentity()), DummyContract.PROGRAM_ID, AlwaysAcceptAttachmentConstraint)</ID>
<ID>MaxLineLength:TimedFlowTests.kt$TimedFlowTests.Companion$defaultParameters = MockNetworkParameters().withServicePeerAllocationStrategy(InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin())</ID> <ID>MaxLineLength:TimedFlowTests.kt$TimedFlowTests.Companion$defaultParameters = MockNetworkParameters().withServicePeerAllocationStrategy(InMemoryMessagingNetwork.ServicePeerAllocationStrategy.RoundRobin())</ID>
@ -3820,13 +3778,6 @@
<ID>MaxLineLength:X509UtilitiesTest.kt$X509UtilitiesTest$val certCaAuthorityKeyIdentifier = AuthorityKeyIdentifier.getInstance(getExtension(Extension.authorityKeyIdentifier).parsedValue)</ID> <ID>MaxLineLength:X509UtilitiesTest.kt$X509UtilitiesTest$val certCaAuthorityKeyIdentifier = AuthorityKeyIdentifier.getInstance(getExtension(Extension.authorityKeyIdentifier).parsedValue)</ID>
<ID>MaxLineLength:X509UtilitiesTest.kt$X509UtilitiesTest.Companion$Triple(ECDSA_SECP256K1_SHA256,java.security.interfaces.ECPrivateKey::class.java, org.bouncycastle.jce.interfaces.ECPrivateKey::class.java)</ID> <ID>MaxLineLength:X509UtilitiesTest.kt$X509UtilitiesTest.Companion$Triple(ECDSA_SECP256K1_SHA256,java.security.interfaces.ECPrivateKey::class.java, org.bouncycastle.jce.interfaces.ECPrivateKey::class.java)</ID>
<ID>MaxLineLength:X509UtilitiesTest.kt$X509UtilitiesTest.Companion$Triple(ECDSA_SECP256R1_SHA256,java.security.interfaces.ECPrivateKey::class.java, org.bouncycastle.jce.interfaces.ECPrivateKey::class.java)</ID> <ID>MaxLineLength:X509UtilitiesTest.kt$X509UtilitiesTest.Companion$Triple(ECDSA_SECP256R1_SHA256,java.security.interfaces.ECPrivateKey::class.java, org.bouncycastle.jce.interfaces.ECPrivateKey::class.java)</ID>
<ID>MaxLineLength:YearMonthSerializer.kt$YearMonthSerializer : Proxy</ID>
<ID>MaxLineLength:YearSerializer.kt$YearSerializer : Proxy</ID>
<ID>MaxLineLength:ZoneIdSerializer.kt$ZoneIdSerializer : Proxy</ID>
<ID>MaxLineLength:ZonedDateTimeSerializer.kt$ZonedDateTimeSerializer : Proxy</ID>
<ID>MaxLineLength:ZonedDateTimeSerializer.kt$ZonedDateTimeSerializer$override fun fromProxy(proxy: ZonedDateTimeProxy): ZonedDateTime</ID>
<ID>MaxLineLength:ZonedDateTimeSerializer.kt$ZonedDateTimeSerializer$override val additionalSerializers: Iterable&lt;CustomSerializer&lt;out Any&gt;&gt; = listOf(LocalDateTimeSerializer(factory), ZoneIdSerializer(factory))</ID>
<ID>MaxLineLength:ZonedDateTimeSerializer.kt$ZonedDateTimeSerializer.Companion$val ofLenient: Method = ZonedDateTime::class.java.getDeclaredMethod("ofLenient", LocalDateTime::class.java, ZoneOffset::class.java, ZoneId::class.java)</ID>
<ID>MaxLineLength:internalAccessTestHelpers.kt$( inputs: List&lt;StateAndRef&lt;ContractState&gt;&gt;, outputs: List&lt;TransactionState&lt;ContractState&gt;&gt;, commands: List&lt;CommandWithParties&lt;CommandData&gt;&gt;, attachments: List&lt;Attachment&gt;, id: SecureHash, notary: Party?, timeWindow: TimeWindow?, privacySalt: PrivacySalt, networkParameters: NetworkParameters, references: List&lt;StateAndRef&lt;ContractState&gt;&gt;, componentGroups: List&lt;ComponentGroup&gt;? = null, serializedInputs: List&lt;SerializedStateAndRef&gt;? = null, serializedReferences: List&lt;SerializedStateAndRef&gt;? = null, isAttachmentTrusted: (Attachment) -&gt; Boolean )</ID> <ID>MaxLineLength:internalAccessTestHelpers.kt$( inputs: List&lt;StateAndRef&lt;ContractState&gt;&gt;, outputs: List&lt;TransactionState&lt;ContractState&gt;&gt;, commands: List&lt;CommandWithParties&lt;CommandData&gt;&gt;, attachments: List&lt;Attachment&gt;, id: SecureHash, notary: Party?, timeWindow: TimeWindow?, privacySalt: PrivacySalt, networkParameters: NetworkParameters, references: List&lt;StateAndRef&lt;ContractState&gt;&gt;, componentGroups: List&lt;ComponentGroup&gt;? = null, serializedInputs: List&lt;SerializedStateAndRef&gt;? = null, serializedReferences: List&lt;SerializedStateAndRef&gt;? = null, isAttachmentTrusted: (Attachment) -&gt; Boolean )</ID>
<ID>MaxLineLength:internalAccessTestHelpers.kt$fun createContractCreationError(txId: SecureHash, contractClass: String, cause: Throwable)</ID> <ID>MaxLineLength:internalAccessTestHelpers.kt$fun createContractCreationError(txId: SecureHash, contractClass: String, cause: Throwable)</ID>
<ID>MaxLineLength:internalAccessTestHelpers.kt$fun createContractRejection(txId: SecureHash, contract: Contract, cause: Throwable)</ID> <ID>MaxLineLength:internalAccessTestHelpers.kt$fun createContractRejection(txId: SecureHash, contract: Contract, cause: Throwable)</ID>
@ -3875,64 +3826,6 @@
<ID>NestedBlockDepth:StatusTransitions.kt$StatusTransitions$ fun verify(tx: LedgerTransaction)</ID> <ID>NestedBlockDepth:StatusTransitions.kt$StatusTransitions$ fun verify(tx: LedgerTransaction)</ID>
<ID>NestedBlockDepth:ThrowableSerializer.kt$ThrowableSerializer$override fun fromProxy(proxy: ThrowableProxy): Throwable</ID> <ID>NestedBlockDepth:ThrowableSerializer.kt$ThrowableSerializer$override fun fromProxy(proxy: ThrowableProxy): Throwable</ID>
<ID>NestedBlockDepth:TransactionVerifierServiceInternal.kt$Verifier$ private fun verifyConstraintsValidity(contractAttachmentsByContract: Map&lt;ContractClassName, ContractAttachment&gt;)</ID> <ID>NestedBlockDepth:TransactionVerifierServiceInternal.kt$Verifier$ private fun verifyConstraintsValidity(contractAttachmentsByContract: Map&lt;ContractClassName, ContractAttachment&gt;)</ID>
<ID>ReturnCount:AbstractPartyDescriptor.kt$AbstractPartyDescriptor$override fun &lt;X : Any&gt; unwrap(value: AbstractParty?, type: Class&lt;X&gt;, options: WrapperOptions): X?</ID>
<ID>ReturnCount:AbstractPartyDescriptor.kt$AbstractPartyDescriptor$override fun &lt;X : Any&gt; wrap(value: X?, options: WrapperOptions): AbstractParty?</ID>
<ID>ReturnCount:Address.kt$Address.Companion$fun &lt;ERROR&gt; validFromRawValue(rawValue: String, mapError: (String) -&gt; ERROR): Validated&lt;Address, ERROR&gt;</ID>
<ID>ReturnCount:Amount.kt$Amount.Companion$ @JvmStatic fun getDisplayTokenSize(token: Any): BigDecimal</ID>
<ID>ReturnCount:AttachmentVersionNumberMigration.kt$AttachmentVersionNumberMigration$override fun execute(database: Database?)</ID>
<ID>ReturnCount:AttachmentsClassLoader.kt$AttachmentsClassLoader$private fun containsClasses(attachment: Attachment): Boolean</ID>
<ID>ReturnCount:BankOfCordaWebApi.kt$BankOfCordaWebApi$ @POST @Path("issue-asset-request") @Consumes(MediaType.APPLICATION_JSON) fun issueAssetRequest(params: IssueRequestParams): Response</ID>
<ID>ReturnCount:BridgeControlListener.kt$BridgeControlListener$private fun processControlMessage(msg: ClientMessage)</ID>
<ID>ReturnCount:ByteArrays.kt$fun hexToBin(ch: Char): Int</ID>
<ID>ReturnCount:ByteBufferStreams.kt$ByteBufferInputStream$@Throws(IOException::class) override fun read(b: ByteArray, offset: Int, length: Int): Int</ID>
<ID>ReturnCount:CheckpointAgent.kt$CheckpointHook$private fun &lt;T&gt; getArrayValue(clazz: Class&lt;T&gt;, value: Any?): String?</ID>
<ID>ReturnCount:CheckpointAgent.kt$CheckpointHook$private fun instrumentClass(clazz: CtClass): CtClass?</ID>
<ID>ReturnCount:CloseableTab.kt$CloseableTab$fun requestClose()</ID>
<ID>ReturnCount:CordaAuthenticationPlugin.kt$CordaAuthenticationPlugin$override fun authenticate(username: String?, credential: String?): AuthInfo</ID>
<ID>ReturnCount:CordaClassResolver.kt$CordaClassResolver$private fun checkClass(type: Class&lt;*&gt;): Registration?</ID>
<ID>ReturnCount:Crypto.kt$Crypto$// Custom key pair generator from an entropy required for various tests. It is similar to deriveKeyPairECDSA, // but the accepted range of the input entropy is more relaxed: // 2 &lt;= entropy &lt; N, where N is the order of base-point G. private fun deriveECDSAKeyPairFromEntropy(signatureScheme: SignatureScheme, entropy: BigInteger): KeyPair</ID>
<ID>ReturnCount:Crypto.kt$Crypto$// Given the domain parameters, this routine deterministically generates an ECDSA key pair // in accordance with X9.62 section 5.2.1 pages 26, 27. private fun deriveKeyPairECDSA(parameterSpec: ECParameterSpec, privateKey: PrivateKey, seed: ByteArray): KeyPair</ID>
<ID>ReturnCount:DBRunnerExtension.kt$DBRunnerExtension$private fun getDatabaseContext(context: ExtensionContext?): TestDatabaseContext?</ID>
<ID>ReturnCount:Emoji.kt$Emoji$fun renderIfSupported(obj: Any): String</ID>
<ID>ReturnCount:FlowLogicRefFactoryImpl.kt$FlowLogicRefFactoryImpl$private fun buildParams(constructor: KFunction&lt;FlowLogic&lt;*&gt;&gt;, args: Map&lt;String, Any?&gt;): HashMap&lt;KParameter, Any?&gt;?</ID>
<ID>ReturnCount:FlowManager.kt$NodeFlowManager.FlowWeightComparator$override fun compare(o1: NodeFlowManager.RegisteredFlowContainer, o2: NodeFlowManager.RegisteredFlowContainer): Int</ID>
<ID>ReturnCount:InteractiveShell.kt$InteractiveShell$ @JvmStatic fun runFlowByNameFragment(nameFragment: String, inputData: String, output: RenderPrintWriter, rpcOps: CordaRPCOps, ansiProgressRenderer: ANSIProgressRenderer, inputObjectMapper: ObjectMapper = createYamlInputMapper(rpcOps))</ID>
<ID>ReturnCount:InteractiveShell.kt$InteractiveShell$@JvmStatic fun runRPCFromString(input: List&lt;String&gt;, out: RenderPrintWriter, context: InvocationContext&lt;out Any&gt;, cordaRPCOps: CordaRPCOps, inputObjectMapper: ObjectMapper): Any?</ID>
<ID>ReturnCount:Interpolators.kt$LinearInterpolator$override fun interpolate(x: Double): Double</ID>
<ID>ReturnCount:JarScanningCordappLoader.kt$JarScanningCordappLoader$private fun parseCordappInfo(manifest: Manifest?, defaultName: String): Cordapp.Info</ID>
<ID>ReturnCount:LocalSerializerFactory.kt$DefaultLocalSerializerFactory$override fun get(actualClass: Class&lt;*&gt;, declaredType: Type): AMQPSerializer&lt;Any&gt;</ID>
<ID>ReturnCount:LocalTypeInformationBuilder.kt$ private fun constructorForDeserialization(type: Type): KFunction&lt;Any&gt;?</ID>
<ID>ReturnCount:LocalTypeInformationBuilder.kt$LocalTypeInformationBuilder$ private fun buildNonAtomic(rawType: Class&lt;*&gt;, type: Type, typeIdentifier: TypeIdentifier, typeParameterInformation: List&lt;LocalTypeInformation&gt;): LocalTypeInformation</ID>
<ID>ReturnCount:LocalTypeInformationBuilder.kt$LocalTypeInformationBuilder$private fun makeConstructorPairedProperty(constructorIndex: Int, descriptor: PropertyDescriptor, constructorInformation: LocalConstructorInformation): LocalPropertyInformation?</ID>
<ID>ReturnCount:Main.kt$Node$fun isAccepted(tx: Transaction): Boolean</ID>
<ID>ReturnCount:MockNodeMessagingService.kt$MockNodeMessagingService$ private fun getNextQueue(q: LinkedBlockingQueue&lt;InMemoryMessagingNetwork.MessageTransfer&gt;, block: Boolean): Pair&lt;InMemoryMessagingNetwork.MessageTransfer, List&lt;Handler&gt;&gt;?</ID>
<ID>ReturnCount:NetworkRegistrationHelper.kt$NodeRegistrationHelper$override fun validateAndGetTlsCrlIssuerCert(): X509Certificate?</ID>
<ID>ReturnCount:NodeAttachmentTrustCalculator.kt$NodeAttachmentTrustCalculator$override fun calculate(attachment: Attachment): Boolean</ID>
<ID>ReturnCount:NodeConfigurationImpl.kt$NodeConfigurationImpl$private fun validateDevModeOptions(): List&lt;String&gt;</ID>
<ID>ReturnCount:NodeSchemaService.kt$NodeSchemaService$// Because schema is always one supported by the state, just delegate. override fun generateMappedObject(state: ContractState, schema: MappedSchema): PersistentState</ID>
<ID>ReturnCount:NodeStartup.kt$NodeStartup$fun initialiseAndRun(cmdLineOptions: SharedNodeCmdLineOptions, afterNodeInitialisation: RunAfterNodeInitialisation, requireCertificates: Boolean = false): Int</ID>
<ID>ReturnCount:NodeStartup.kt$NodeStartup$fun isNodeRunningAt(baseDirectory: Path): Boolean</ID>
<ID>ReturnCount:NodeStartup.kt$NodeStartup$private fun canReadCertificatesDirectory(certDirectory: Path, devMode: Boolean): Boolean</ID>
<ID>ReturnCount:NodeStartup.kt$fun CliWrapperBase.initLogging(baseDirectory: Path): Boolean</ID>
<ID>ReturnCount:NodeVaultService.kt$NodeVaultService$@Suspendable @Throws(StatesNotAvailableException::class) override fun &lt;T : FungibleState&lt;*&gt;&gt; tryLockFungibleStatesForSpending( lockId: UUID, eligibleStatesQuery: QueryCriteria, amount: Amount&lt;*&gt;, contractStateType: Class&lt;out T&gt; ): List&lt;StateAndRef&lt;T&gt;&gt;</ID>
<ID>ReturnCount:ObjectDiffer.kt$ObjectDiffer$fun diff(a: Any?, b: Any?): DiffTree?</ID>
<ID>ReturnCount:PartialMerkleTree.kt$PartialMerkleTree$// Helper function to compute the path. False means go to the left and True to the right. // Because the path is updated recursively, the path is returned in reverse order. private fun leafIndexHelper(leaf: SecureHash, node: PartialTree, path: MutableList&lt;Boolean&gt;): Boolean</ID>
<ID>ReturnCount:PersistentNetworkMapCache.kt$PersistentNetworkMapCache$override fun getPartyInfo(party: Party): PartyInfo?</ID>
<ID>ReturnCount:RPCClientProxyHandler.kt$RPCClientProxyHandler$// This is the general function that transforms a client side RPC to internal Artemis messages. override fun invoke(proxy: Any, method: Method, arguments: Array&lt;out Any?&gt;?): Any?</ID>
<ID>ReturnCount:RPCSecurityManagerImpl.kt$RPCPermissionResolver$override fun resolvePermission(representation: String): Permission</ID>
<ID>ReturnCount:RigorousMock.kt$SpectatorDefaultAnswer$override fun answerImpl(invocation: InvocationOnMock): Any?</ID>
<ID>ReturnCount:SerialFilter.kt$SerialFilter$internal fun applyPredicate(acceptClass: (Class&lt;*&gt;) -&gt; Boolean, serialClass: Class&lt;*&gt;?): Boolean</ID>
<ID>ReturnCount:ServicesForResolutionImpl.kt$ServicesForResolutionImpl$// We may need to recursively chase transactions if there are notary changes. fun inner(stateRef: StateRef, forContractClassName: String?): Attachment</ID>
<ID>ReturnCount:SingleThreadedStateMachineManager.kt$SingleThreadedStateMachineManager$override fun retryFlowFromSafePoint(currentState: StateMachineState)</ID>
<ID>ReturnCount:SingleThreadedStateMachineManager.kt$SingleThreadedStateMachineManager$private fun createFlowFromCheckpoint( id: StateMachineRunId, serializedCheckpoint: SerializedBytes&lt;Checkpoint&gt;, isAnyCheckpointPersisted: Boolean, isStartIdempotent: Boolean, initialDeduplicationHandler: DeduplicationHandler? ): Flow?</ID>
<ID>ReturnCount:SpecificationTest.kt$SpecificationTest$fun parseMax(elements: List&lt;Long&gt;): Valid&lt;Long&gt;</ID>
<ID>ReturnCount:StandaloneShell.kt$StandaloneShell$override fun runProgram(): Int</ID>
<ID>ReturnCount:TransactionBuilder.kt$TransactionBuilder$ private fun handleContract( contractClassName: ContractClassName, inputStates: List&lt;TransactionState&lt;ContractState&gt;&gt;?, outputStates: List&lt;TransactionState&lt;ContractState&gt;&gt;?, explicitContractAttachment: AttachmentId?, services: ServicesForResolution ): Pair&lt;AttachmentId, List&lt;TransactionState&lt;ContractState&gt;&gt;?&gt;</ID>
<ID>ReturnCount:TransactionUtils.kt$ fun &lt;T : Any&gt; deserialiseComponentGroup(componentGroups: List&lt;ComponentGroup&gt;, clazz: KClass&lt;T&gt;, groupEnum: ComponentGroupEnum, forceDeserialize: Boolean = false, factory: SerializationFactory = SerializationFactory.defaultFactory, context: SerializationContext = factory.defaultContext): List&lt;T&gt;</ID>
<ID>ReturnCount:TransitionExecutorImpl.kt$TransitionExecutorImpl$@Suspendable override fun executeTransition( fiber: FlowFiber, previousState: StateMachineState, event: Event, transition: TransitionResult, actionExecutor: ActionExecutor ): Pair&lt;FlowContinuation, StateMachineState&gt;</ID>
<ID>ReturnCount:TypeParameterUtils.kt$ private fun inferTypeVariables(actualClass: Class&lt;*&gt;, declaredClass: Class&lt;*&gt;, declaredType: ParameterizedType): Type?</ID>
<ID>ReturnCount:Util.kt$fun &lt;T&gt; debugCompare(perLeft: Perceivable&lt;T&gt;, perRight: Perceivable&lt;T&gt;)</ID>
<ID>ReturnCount:Util.kt$fun debugCompare(arrLeft: Arrangement, arrRight: Arrangement)</ID>
<ID>SpreadOperator:AMQPSerializationScheme.kt$AbstractAMQPSerializationScheme$(*it.whitelist.toTypedArray())</ID> <ID>SpreadOperator:AMQPSerializationScheme.kt$AbstractAMQPSerializationScheme$(*it.whitelist.toTypedArray())</ID>
<ID>SpreadOperator:AbstractNode.kt$FlowStarterImpl$(logicType, *args)</ID> <ID>SpreadOperator:AbstractNode.kt$FlowStarterImpl$(logicType, *args)</ID>
<ID>SpreadOperator:AbstractParty.kt$AbstractParty$(*bytes)</ID> <ID>SpreadOperator:AbstractParty.kt$AbstractParty$(*bytes)</ID>
@ -3969,7 +3862,6 @@
<ID>SpreadOperator:DemoBench.kt$DemoBench.Companion$(DemoBench::class.java, *args)</ID> <ID>SpreadOperator:DemoBench.kt$DemoBench.Companion$(DemoBench::class.java, *args)</ID>
<ID>SpreadOperator:DevCertificatesTest.kt$DevCertificatesTest$(*oldX509Certificates)</ID> <ID>SpreadOperator:DevCertificatesTest.kt$DevCertificatesTest$(*oldX509Certificates)</ID>
<ID>SpreadOperator:DockerInstantiator.kt$DockerInstantiator$(*it.toTypedArray())</ID> <ID>SpreadOperator:DockerInstantiator.kt$DockerInstantiator$(*it.toTypedArray())</ID>
<ID>SpreadOperator:DriverDSLImpl.kt$DriverDSLImpl$( config, quasarJarPath, debugPort, systemProperties, "512m", null, *extraCmdLineFlag )</ID>
<ID>SpreadOperator:DummyContract.kt$DummyContract.Companion$( /* INPUTS */ *priors.toTypedArray(), /* COMMAND */ Command(cmd, priorState.owner.owningKey), /* OUTPUT */ StateAndContract(state, PROGRAM_ID) )</ID> <ID>SpreadOperator:DummyContract.kt$DummyContract.Companion$( /* INPUTS */ *priors.toTypedArray(), /* COMMAND */ Command(cmd, priorState.owner.owningKey), /* OUTPUT */ StateAndContract(state, PROGRAM_ID) )</ID>
<ID>SpreadOperator:DummyContract.kt$DummyContract.Companion$(*items)</ID> <ID>SpreadOperator:DummyContract.kt$DummyContract.Companion$(*items)</ID>
<ID>SpreadOperator:DummyContractV2.kt$DummyContractV2.Companion$( /* INPUTS */ *priors.toTypedArray(), /* COMMAND */ Command(cmd, priorState.owners.map { it.owningKey }), /* OUTPUT */ StateAndContract(state, DummyContractV2.PROGRAM_ID) )</ID> <ID>SpreadOperator:DummyContractV2.kt$DummyContractV2.Companion$( /* INPUTS */ *priors.toTypedArray(), /* COMMAND */ Command(cmd, priorState.owners.map { it.owningKey }), /* OUTPUT */ StateAndContract(state, DummyContractV2.PROGRAM_ID) )</ID>
@ -4528,7 +4420,6 @@
<ID>WildcardImport:AMQPClientSerializationScheme.kt$import net.corda.serialization.internal.*</ID> <ID>WildcardImport:AMQPClientSerializationScheme.kt$import net.corda.serialization.internal.*</ID>
<ID>WildcardImport:AMQPClientSerializationScheme.kt$import net.corda.serialization.internal.amqp.*</ID> <ID>WildcardImport:AMQPClientSerializationScheme.kt$import net.corda.serialization.internal.amqp.*</ID>
<ID>WildcardImport:AMQPRemoteTypeModel.kt$import net.corda.serialization.internal.model.*</ID> <ID>WildcardImport:AMQPRemoteTypeModel.kt$import net.corda.serialization.internal.model.*</ID>
<ID>WildcardImport:AMQPSerializationScheme.kt$import net.corda.core.internal.*</ID>
<ID>WildcardImport:AMQPSerializationScheme.kt$import net.corda.core.serialization.*</ID> <ID>WildcardImport:AMQPSerializationScheme.kt$import net.corda.core.serialization.*</ID>
<ID>WildcardImport:AMQPServerSerializationScheme.kt$import net.corda.serialization.internal.amqp.*</ID> <ID>WildcardImport:AMQPServerSerializationScheme.kt$import net.corda.serialization.internal.amqp.*</ID>
<ID>WildcardImport:AMQPTestSerialiationScheme.kt$import net.corda.serialization.internal.amqp.*</ID> <ID>WildcardImport:AMQPTestSerialiationScheme.kt$import net.corda.serialization.internal.amqp.*</ID>
@ -4971,7 +4862,6 @@
<ID>WildcardImport:NodeInterestRates.kt$import net.corda.core.flows.*</ID> <ID>WildcardImport:NodeInterestRates.kt$import net.corda.core.flows.*</ID>
<ID>WildcardImport:NodeInterestRatesTest.kt$import net.corda.testing.core.*</ID> <ID>WildcardImport:NodeInterestRatesTest.kt$import net.corda.testing.core.*</ID>
<ID>WildcardImport:NodeInterestRatesTest.kt$import org.junit.Assert.*</ID> <ID>WildcardImport:NodeInterestRatesTest.kt$import org.junit.Assert.*</ID>
<ID>WildcardImport:NodeMonitorModel.kt$import net.corda.core.messaging.*</ID>
<ID>WildcardImport:NodeProcess.kt$import net.corda.core.internal.*</ID> <ID>WildcardImport:NodeProcess.kt$import net.corda.core.internal.*</ID>
<ID>WildcardImport:NodeRegistrationTest.kt$import javax.ws.rs.*</ID> <ID>WildcardImport:NodeRegistrationTest.kt$import javax.ws.rs.*</ID>
<ID>WildcardImport:NodeSchedulerService.kt$import net.corda.core.internal.*</ID> <ID>WildcardImport:NodeSchedulerService.kt$import net.corda.core.internal.*</ID>

View File

@ -183,12 +183,6 @@ style:
OptionalAbstractKeyword: OptionalAbstractKeyword:
active: true active: true
excludes: "**/buildSrc/**" excludes: "**/buildSrc/**"
ReturnCount:
active: true
excludes: "**/buildSrc/**"
max: 2
excludedFunctions: "equals"
excludeReturnFromLambda: true
SafeCast: SafeCast:
active: true active: true
excludes: "**/buildSrc/**" excludes: "**/buildSrc/**"

View File

@ -37,6 +37,7 @@ shadowJar {
classifier = null classifier = null
version = null version = null
zip64 true zip64 true
exclude '**/Log4j2Plugins.dat'
} }
docker{ docker{

View File

@ -60,8 +60,7 @@ function downloadTestnetCerts() {
: ${ONE_TIME_DOWNLOAD_KEY:? '$ONE_TIME_DOWNLOAD_KEY must be set as environment variable'} : ${ONE_TIME_DOWNLOAD_KEY:? '$ONE_TIME_DOWNLOAD_KEY must be set as environment variable'}
: ${LOCALITY:? '$LOCALITY (the locality used when registering for Testnet) must be set as environment variable'} : ${LOCALITY:? '$LOCALITY (the locality used when registering for Testnet) must be set as environment variable'}
: ${COUNTRY:? '$COUNTRY (the country used when registering for Testnet) must be set as environment variable'} : ${COUNTRY:? '$COUNTRY (the country used when registering for Testnet) must be set as environment variable'}
curl -L -d "{\"x500Name\":{\"locality\":\"${LOCALITY}\", \"country\":\"${COUNTRY}\"}, \"configType\": \"INSTALLSCRIPT\", \"include\": { \"systemdServices\": false, \"cordapps\": false, \"cordaJar\": false, \"cordaWebserverJar\": false, \"scripts\": false} }" \ curl \
-H 'Content-Type: application/json' \
-X POST "https://onboarder.prod.ws.r3.com/api/user/node/generate/one-time-key/redeem/$ONE_TIME_DOWNLOAD_KEY" \ -X POST "https://onboarder.prod.ws.r3.com/api/user/node/generate/one-time-key/redeem/$ONE_TIME_DOWNLOAD_KEY" \
-o "${CERTIFICATES_FOLDER}/certs.zip" -o "${CERTIFICATES_FOLDER}/certs.zip"
fi fi

View File

@ -27,6 +27,7 @@ We currently support the following placeholders; they get substituted with the c
```groovy ```groovy
"|corda_version|" "|corda_version|"
"|corda_version_lower|"
"|java_version|" "|java_version|"
"|kotlin_version|" "|kotlin_version|"
"|gradle_plugins_version|" "|gradle_plugins_version|"
@ -34,6 +35,7 @@ We currently support the following placeholders; they get substituted with the c
``` ```
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` 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. (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 code for this can be found near the top of the conf.py file in the `docs/source` directory.

View File

@ -88,6 +88,9 @@ Unreleased
Note that it's a responsibility of a client application to handle RPC reconnection in case this happens. Note that it's a responsibility of a client application to handle RPC reconnection in case this happens.
See :ref:`setting_jvm_args` and :ref:`memory_usage_and_tuning` for further details. See :ref:`setting_jvm_args` and :ref:`memory_usage_and_tuning` for further details.
* Environment variables and system properties can now be provided with underscore separators instead of dots. Neither are case sensitive.
See :ref:`overriding config values <corda_configuration_file_overriding_config>` for more information.
.. _changelog_v4.1: .. _changelog_v4.1:
Version 4.1 Version 4.1

View File

@ -371,13 +371,19 @@ More specifically, the behaviour in the second case is a bit more subtle:
.. warning:: In this approach, some events might be lost during a reconnection and not sent from the subscribed ``Observable``\s. .. warning:: In this approach, some events might be lost during a reconnection and not sent from the subscribed ``Observable``\s.
You can enable this graceful form of reconnection by using the ``gracefulReconnect`` parameter in the following way: You can enable this graceful form of reconnection by using the ``gracefulReconnect`` parameter, which is an object containing 3 optional fields:
* ``onDisconnect``: A callback handler that will be invoked every time the connection is disconnected.
* ``onReconnect``: A callback handler that will be invoked every time the connection is established again after a disconnection.
* ``maxAttempts``: The maximum number of attempts that will be performed per RPC operation. A negative value implies infinite retries. The default value is 5.
This can be used in the following way:
.. container:: codeset .. container:: codeset
.. sourcecode:: kotlin .. sourcecode:: kotlin
val gracefulReconnect = GracefulReconnect(onDisconnect={/*insert disconnect handling*/}, onReconnect{/*insert reconnect handling*/}) val gracefulReconnect = GracefulReconnect(onDisconnect={/*insert disconnect handling*/}, onReconnect{/*insert reconnect handling*/}, maxAttempts = 3)
val cordaClient = CordaRPCClient(nodeRpcAddress) val cordaClient = CordaRPCClient(nodeRpcAddress)
val cordaRpcOps = cordaClient.start(rpcUserName, rpcUserPassword, gracefulReconnect = gracefulReconnect).proxy val cordaRpcOps = cordaClient.start(rpcUserName, rpcUserPassword, gracefulReconnect = gracefulReconnect).proxy
@ -392,7 +398,7 @@ You can enable this graceful form of reconnection by using the ``gracefulReconne
} }
void method() { void method() {
GracefulReconnect gracefulReconnect = new GracefulReconnect(this::onDisconnect, this::onReconnect); GracefulReconnect gracefulReconnect = new GracefulReconnect(this::onDisconnect, this::onReconnect, 3);
CordaRPCClient cordaClient = new CordaRPCClient(nodeRpcAddress); CordaRPCClient cordaClient = new CordaRPCClient(nodeRpcAddress);
CordaRPCConnection cordaRpcOps = cordaClient.start(rpcUserName, rpcUserPassword, gracefulReconnect); CordaRPCConnection cordaRpcOps = cordaClient.start(rpcUserName, rpcUserPassword, gracefulReconnect);
} }

View File

@ -19,6 +19,7 @@ def cordaSourceReadReplace(app, docname, source):
corda_substitutions = { corda_substitutions = {
"|corda_version|" : constants_properties_dict["cordaVersion"], "|corda_version|" : constants_properties_dict["cordaVersion"],
"|corda_version_lower|" : constants_properties_dict["cordaVersion"].lower(),
"|java_version|" : "8u"+constants_properties_dict["java8MinUpdateVersion"], "|java_version|" : "8u"+constants_properties_dict["java8MinUpdateVersion"],
"|kotlin_version|" : constants_properties_dict["kotlinVersion"], "|kotlin_version|" : constants_properties_dict["kotlinVersion"],
"|gradle_plugins_version|" : constants_properties_dict["gradlePluginsVersion"], "|gradle_plugins_version|" : constants_properties_dict["gradlePluginsVersion"],

View File

@ -39,6 +39,8 @@ To alter this behaviour, the ``on-unknown-config-keys`` command-line argument ca
Overriding values from node.conf Overriding values from node.conf
-------------------------------- --------------------------------
.. _corda_configuration_file_overriding_config:
Environment variables Environment variables
For example: ``${NODE_TRUST_STORE_PASSWORD}`` would be replaced by the contents of environment variable ``NODE_TRUST_STORE_PASSWORD`` (see: :ref:`hiding-sensitive-data` section). For example: ``${NODE_TRUST_STORE_PASSWORD}`` would be replaced by the contents of environment variable ``NODE_TRUST_STORE_PASSWORD`` (see: :ref:`hiding-sensitive-data` section).
@ -54,6 +56,11 @@ JVM options
.. note:: If the same field is overriden by both an environment variable and system property, the system property .. note:: If the same field is overriden by both an environment variable and system property, the system property
takes precedence. takes precedence.
.. note:: Underscores can be used in instead of dots. For example overriding the ``p2pAddress`` with an environment variable can be done
by specifying ``CORDA_P2PADDRESS=host:port``. Variables and properties are not case sensitive. Corda will warn you if a variable
prefixed with ``CORDA`` cannot be mapped to a valid property. Shadowing occurs when two properties
of the same type with the same key are defined. For example having ``CORDA_P2PADDRESS=host:port`` and ``corda_p2paddress=host1:port1``
will raise an exception on startup. This is to prevent hard to spot mistakes.
Configuration file fields Configuration file fields
------------------------- -------------------------

View File

@ -342,7 +342,7 @@ It is also important that we capture the correct amount of contextual informatio
- Flow id (runId, also referred to as `StateMachineRunId`), if logging within a flow - Flow id (runId, also referred to as `StateMachineRunId`), if logging within a flow
- Other contextual Flow information (eg. counterparty), if logging within a flow - Other contextual Flow information (eg. counterparty), if logging within a flow
- `FlowStackSnapshot` information for catastrophic flow failures. - `FlowStackSnapshot` information for catastrophic flow failures.
Note: this information is not currently supposed to be used in production (???). Note: this information is not currently supposed to be used in production.
- Session id information for RPC calls - Session id information for RPC calls
- CorDapp name, if logging from within a CorDapp - CorDapp name, if logging from within a CorDapp

View File

@ -21,7 +21,7 @@ In this example, the certificates are stored at ``/home/user/cordaBase/certifica
-v /path/to/cordapps:/opt/corda/cordapps \ -v /path/to/cordapps:/opt/corda/cordapps \
-p 10200:10200 \ -p 10200:10200 \
-p 10201:10201 \ -p 10201:10201 \
corda/corda-zulu-4.4-snapshot:latest corda/corda-zulu-java1.8-|corda_version_lower|:latest
As the node runs within a container, several mount points are required: As the node runs within a container, several mount points are required:
@ -59,7 +59,7 @@ In this example, we have previously generated a network-parameters file using th
-v /home/user/sharedFolder/network-parameters:/opt/corda/network-parameters \ -v /home/user/sharedFolder/network-parameters:/opt/corda/network-parameters \
-p 10200:10200 \ -p 10200:10200 \
-p 10201:10201 \ -p 10201:10201 \
corda/corda-zulu-4.4-snapshot:latest corda/corda-zulu-java1.8-|corda_version_lower|:latest
There is a new mount ``/home/user/sharedFolder/node-infos:/opt/corda/additional-node-infos`` which is used to hold the ``nodeInfo`` of all the nodes within the network. There is a new mount ``/home/user/sharedFolder/node-infos:/opt/corda/additional-node-infos`` which is used to hold the ``nodeInfo`` of all the nodes within the network.
As the node within the container starts up, it will place it's own nodeInfo into this directory. This will allow other nodes also using this folder to see this new node. As the node within the container starts up, it will place it's own nodeInfo into this directory. This will allow other nodes also using this folder to see this new node.
@ -83,7 +83,7 @@ Joining TestNet
-e LOCALITY="London" -e COUNTRY="GB" \ -e LOCALITY="London" -e COUNTRY="GB" \
-v /home/user/docker/config:/etc/corda \ -v /home/user/docker/config:/etc/corda \
-v /home/user/docker/certificates:/opt/corda/certificates \ -v /home/user/docker/certificates:/opt/corda/certificates \
corda/corda-zulu-4.4-snapshot:latest config-generator --testnet corda/corda-zulu-java1.8-|corda_version_lower|:latest config-generator --testnet
``$MY_PUBLIC_ADDRESS`` will be the public address that this node will be advertised on. ``$MY_PUBLIC_ADDRESS`` will be the public address that this node will be advertised on.
``$ONE_TIME_DOWNLOAD_KEY`` is the one-time code provided for joining TestNet. ``$ONE_TIME_DOWNLOAD_KEY`` is the one-time code provided for joining TestNet.
@ -108,7 +108,7 @@ It is now possible to start the node using the generated config and certificates
-v /home/user/corda/samples/bank-of-corda-demo/build/nodes/BankOfCorda/cordapps:/opt/corda/cordapps \ -v /home/user/corda/samples/bank-of-corda-demo/build/nodes/BankOfCorda/cordapps:/opt/corda/cordapps \
-p 10200:10200 \ -p 10200:10200 \
-p 10201:10201 \ -p 10201:10201 \
corda/corda-zulu-4.4-snapshot:latest corda/corda-zulu-java1.8-|corda_version_lower|:latest
Joining an existing Compatibility Zone Joining an existing Compatibility Zone
@ -132,7 +132,7 @@ It is possible to configure the name of the Trust Root file by setting the ``TRU
-e MY_EMAIL_ADDRESS="cordauser@r3.com" \ -e MY_EMAIL_ADDRESS="cordauser@r3.com" \
-v /home/user/docker/config:/etc/corda \ -v /home/user/docker/config:/etc/corda \
-v /home/user/docker/certificates:/opt/corda/certificates \ -v /home/user/docker/certificates:/opt/corda/certificates \
corda/corda-zulu-4.4-snapshot:latest config-generator --generic corda/corda-zulu-java1.8-|corda_version_lower|:latest config-generator --generic
Several environment variables must also be passed to the container to allow it to register: Several environment variables must also be passed to the container to allow it to register:
@ -163,5 +163,5 @@ Once the container has finished performing the initial registration, the node ca
-v /home/user/corda/samples/bank-of-corda-demo/build/nodes/BankOfCorda/cordapps:/opt/corda/cordapps \ -v /home/user/corda/samples/bank-of-corda-demo/build/nodes/BankOfCorda/cordapps:/opt/corda/cordapps \
-p 10200:10200 \ -p 10200:10200 \
-p 10201:10201 \ -p 10201:10201 \
corda/corda-zulu-4.4-snapshot:latest corda/corda-zulu-java1.8-|corda_version_lower|:latest

View File

@ -10,7 +10,7 @@ containers to abstract the complexity of managing a distributed network away fro
The network you build will either be made up of local ``Docker`` nodes *or* of nodes spread across Azure The network you build will either be made up of local ``Docker`` nodes *or* of nodes spread across Azure
containers. containers.
For each node a separate Docker image is built based on `corda/corda-zulu-|corda_version| <https://hub.docker.com/r/corda/corda-zulu-|corda_version|>`_. For each node a separate Docker image is built based on `corda/corda-zulu-java1.8-|corda_version| <https://hub.docker.com/r/corda/corda-zulu-java1.8-|corda_version_lower|>`_.
Unlike the official image, a `node.conf` file and CorDapps are embedded into the image Unlike the official image, a `node.conf` file and CorDapps are embedded into the image
(they are not externally provided to the running container via volumes/mount points). (they are not externally provided to the running container via volumes/mount points).
More backends may be added in future. The tool is open source, so contributions to add more More backends may be added in future. The tool is open source, so contributions to add more

View File

@ -195,20 +195,20 @@ parameters will be accepted without user input. The following parameters with th
:start-after: DOCSTART 1 :start-after: DOCSTART 1
:end-before: DOCEND 1 :end-before: DOCEND 1
This behaviour can be turned off by setting the optional node configuration property ``NetworkParameterAcceptanceSettings.autoAcceptEnabled`` This behaviour can be turned off by setting the optional node configuration property ``networkParameterAcceptanceSettings.autoAcceptEnabled``
to ``false``. For example: to ``false``. For example:
.. sourcecode:: guess .. sourcecode:: guess
... ...
NetworkParameterAcceptanceSettings { networkParameterAcceptanceSettings {
autoAcceptEnabled = false autoAcceptEnabled = false
} }
... ...
It is also possible to switch off this behaviour at a more granular parameter level. This can be achieved by specifying the set of It is also possible to switch off this behaviour at a more granular parameter level. This can be achieved by specifying the set of
``@AutoAcceptable`` parameters that should not be auto-acceptable in the optional ``@AutoAcceptable`` parameters that should not be auto-acceptable in the optional
``NetworkParameterAcceptanceSettings.excludedAutoAcceptableParameters`` node configuration property. ``networkParameterAcceptanceSettings.excludedAutoAcceptableParameters`` node configuration property.
For example, auto-acceptance can be switched off for any updates that change the ``packageOwnership`` map by adding the following to the For example, auto-acceptance can be switched off for any updates that change the ``packageOwnership`` map by adding the following to the
node configuration: node configuration:
@ -216,7 +216,7 @@ node configuration:
.. sourcecode:: guess .. sourcecode:: guess
... ...
NetworkParameterAcceptanceSettings { networkParameterAcceptanceSettings {
excludedAutoAcceptableParameters: ["packageOwnership"] excludedAutoAcceptableParameters: ["packageOwnership"]
} }
... ...

View File

@ -71,25 +71,41 @@ PARTY_NAME FK to NODE_INFO_PARTY_CERT
Node identities Node identities
^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^
The following two tables are used by the ``IdentityService`` and are created from the NodeInfos. The following four tables are used by the ``IdentityService`` and are created from the NodeInfos.
They are append only tables used for persistent caching. They are append only tables used for persistent caching.
They will also be cleared on ``rpc.clearNetworkMapCache()``. They will also be cleared on ``rpc.clearNetworkMapCache()``.
Read more in :doc:`api-identity` and :doc:`node-services` Read more in :doc:`api-identity` and :doc:`node-services`
============================== ========================================================================================== ============================== ==========================================================================================
NODE_IDENTITIES Maps public keys to identities NODE_IDENTITIES Maps a public key hash to an identity.
============================== ========================================================================================== ============================== ==========================================================================================
PK_HASH The public key. PK_HASH The public key hash.
IDENTITY_VALUE The certificate chain. IDENTITY_VALUE The certificate chain.
============================== ========================================================================================== ============================== ==========================================================================================
============================== ========================================================================================== ============================== ==========================================================================================
NODE_NAMED_IDENTITIES Maps the X500 name of a participant to a public key. NODE_NAMED_IDENTITIES Maps the X500 name of a participant to a public key hash.
============================== ========================================================================================== ============================== ==========================================================================================
NAME The x500 name NAME The x500 name.
PK_HASH The public key PK_HASH The public key hash.
============================== ==========================================================================================
============================== ==========================================================================================
NODE_IDENTITIES_NO_CERT Maps a public key hash to the X500 name of a participant.
============================== ==========================================================================================
PK_HASH The public key hash.
NAME The x500 name.
============================== ==========================================================================================
============================== ==========================================================================================
NODE_HASH_TO_KEY Maps a public key hash to a public key.
============================== ==========================================================================================
PK_HASH The public key hash.
PUBLIC_KEY The public key.
============================== ========================================================================================== ============================== ==========================================================================================
@ -173,6 +189,7 @@ NODE_TRANSACTIONS Corda transactions in a binary format
TX_ID The hash of the transaction. Primary key. TX_ID The hash of the transaction. Primary key.
TRANSACTION_VALUE The binary representation of the transaction. TRANSACTION_VALUE The binary representation of the transaction.
STATE_MACHINE_RUN_ID The flow id associated with this transaction. STATE_MACHINE_RUN_ID The flow id associated with this transaction.
STATUS ``VERIFIED`` or ``UNVERIFIED``
============================== ========================================================================================== ============================== ==========================================================================================
| |
@ -224,7 +241,6 @@ PUBLIC_KEY Binary public key
============================== ========================================================================================== ============================== ==========================================================================================
PK_HASH_TO_EXT_ID_MAP Maps public keys to external ids. Mainly used by CorDapps that need to simulate accounts. PK_HASH_TO_EXT_ID_MAP Maps public keys to external ids. Mainly used by CorDapps that need to simulate accounts.
============================== ========================================================================================== ============================== ==========================================================================================
ID Primary key
EXTERNAL_ID External id EXTERNAL_ID External id
PUBLIC_KEY_HASH Public key hash PUBLIC_KEY_HASH Public key hash
============================== ========================================================================================== ============================== ==========================================================================================

View File

@ -0,0 +1,87 @@
package net.corda.node
import net.corda.core.utilities.getOrThrow
import net.corda.node.logging.logFile
import net.corda.testing.driver.DriverParameters
import net.corda.testing.driver.NodeParameters
import net.corda.testing.driver.driver
import net.corda.testing.driver.internal.incrementalPortAllocation
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.Test
import org.junit.jupiter.api.assertThrows
class NodeConfigParsingTests {
@Test
fun `config is overriden by underscore variable`() {
val portAllocator = incrementalPortAllocation()
val sshPort = portAllocator.nextPort()
driver(DriverParameters(
environmentVariables = mapOf("corda_sshd_port" to sshPort.toString()),
startNodesInProcess = false,
portAllocation = portAllocator)) {
val hasSsh = startNode().get()
.logFile()
.readLines()
.filter { it.contains("SSH server listening on port") }
.any { it.contains(sshPort.toString()) }
assert(hasSsh)
}
}
@Test
fun `config is overriden by case insensitive underscore variable`() {
val portAllocator = incrementalPortAllocation()
val sshPort = portAllocator.nextPort()
driver(DriverParameters(
environmentVariables = mapOf("cORDa_sShD_pOrt" to sshPort.toString()),
startNodesInProcess = false,
portAllocation = portAllocator)) {
val hasSsh = startNode().get()
.logFile()
.readLines()
.filter { it.contains("SSH server listening on port") }
.any { it.contains(sshPort.toString()) }
assert(hasSsh)
}
}
@Test
fun `config is overriden by case insensitive dot variable`() {
val portAllocator = incrementalPortAllocation()
val sshPort = portAllocator.nextPort()
driver(DriverParameters(
environmentVariables = mapOf("cOrda.sShD.pOrt" to sshPort.toString()),
startNodesInProcess = false,
portAllocation = portAllocator)) {
val hasSsh = startNode(NodeParameters()).get()
.logFile()
.readLines()
.filter { it.contains("SSH server listening on port") }
.any { it.contains(sshPort.toString()) }
assert(hasSsh)
}
}
@Test
fun `shadowing is forbidden`() {
val portAllocator = incrementalPortAllocation()
val sshPort = portAllocator.nextPort()
driver(DriverParameters(
environmentVariables = mapOf(
"cOrda_sShD_POrt" to sshPort.toString(),
"cOrda.sShD.pOrt" to sshPort.toString()),
startNodesInProcess = false,
portAllocation = portAllocator,
notarySpecs = emptyList())) {
assertThatThrownBy {
startNode().getOrThrow()
}
}
}
}

View File

@ -10,7 +10,6 @@ import org.junit.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertTrue import kotlin.test.assertTrue
@Ignore
class NodeRPCTests { class NodeRPCTests {
private val CORDA_VERSION_REGEX = "\\d+(\\.\\d+)?(-\\w+)?".toRegex() private val CORDA_VERSION_REGEX = "\\d+(\\.\\d+)?(-\\w+)?".toRegex()
private val CORDA_VENDOR = "Corda Open Source" private val CORDA_VENDOR = "Corda Open Source"
@ -28,7 +27,6 @@ class NodeRPCTests {
driver(DriverParameters(notarySpecs = emptyList(), cordappsForAllNodes = CORDAPPS, extraCordappPackagesToScan = emptyList())) { driver(DriverParameters(notarySpecs = emptyList(), cordappsForAllNodes = CORDAPPS, extraCordappPackagesToScan = emptyList())) {
val nodeDiagnosticInfo = startNode().get().rpc.nodeDiagnosticInfo() val nodeDiagnosticInfo = startNode().get().rpc.nodeDiagnosticInfo()
assertTrue(nodeDiagnosticInfo.version.matches(CORDA_VERSION_REGEX)) assertTrue(nodeDiagnosticInfo.version.matches(CORDA_VERSION_REGEX))
assertTrue(nodeDiagnosticInfo.revision.matches(HEXADECIMAL_REGEX))
assertEquals(PLATFORM_VERSION, nodeDiagnosticInfo.platformVersion) assertEquals(PLATFORM_VERSION, nodeDiagnosticInfo.platformVersion)
assertEquals(CORDA_VENDOR, nodeDiagnosticInfo.vendor) assertEquals(CORDA_VENDOR, nodeDiagnosticInfo.vendor)
nodeDiagnosticInfo.cordapps.forEach { println("${it.shortName} ${it.type}") } nodeDiagnosticInfo.cordapps.forEach { println("${it.shortName} ${it.type}") }

View File

@ -0,0 +1,165 @@
package net.corda.node.services.statemachine
import co.paralleluniverse.fibers.Suspendable
import net.corda.client.rpc.CordaRPCClient
import net.corda.core.contracts.PartyAndReference
import net.corda.core.contracts.StateAndRef
import net.corda.core.flows.FinalityFlow
import net.corda.core.flows.FlowException
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.NotaryException
import net.corda.core.flows.ReceiveFinalityFlow
import net.corda.core.flows.StartableByRPC
import net.corda.core.identity.Party
import net.corda.core.messaging.StateMachineUpdate
import net.corda.core.messaging.startFlow
import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.getOrThrow
import net.corda.node.services.Permissions
import net.corda.testing.contracts.DummyContract
import net.corda.testing.contracts.DummyContract.SingleOwnerState
import net.corda.testing.core.ALICE_NAME
import net.corda.testing.core.CHARLIE_NAME
import net.corda.testing.driver.driver
import net.corda.testing.node.User
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.Test
import java.util.Random
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
class FlowHospitalTest {
private val rpcUser = User("user1", "test", permissions = setOf(Permissions.all()))
@Test
fun `when double spend occurs, the flow is successfully deleted on the counterparty`() {
driver {
val charlie = startNode(providedName = CHARLIE_NAME, rpcUsers = listOf(rpcUser)).getOrThrow()
val alice = startNode(providedName = ALICE_NAME, rpcUsers = listOf(rpcUser)).getOrThrow()
val charlieClient = CordaRPCClient(charlie.rpcAddress).start(rpcUser.username, rpcUser.password).proxy
val aliceClient = CordaRPCClient(alice.rpcAddress).start(rpcUser.username, rpcUser.password).proxy
val aliceParty = aliceClient.nodeInfo().legalIdentities.first()
val (firstLatch, secondLatch) = arrayOf(CountDownLatch(1), CountDownLatch(1))
// case 1: the notary exception is not caught
val stateAndRef = charlieClient.startFlow(::IssueFlow, defaultNotaryIdentity).returnValue.get()
charlieClient.startFlow(::SpendFlow, stateAndRef, aliceParty).returnValue.get()
val firstSubscription = aliceClient.stateMachinesFeed().updates.subscribe {
if (it is StateMachineUpdate.Removed && it.result.isFailure)
firstLatch.countDown()
}
assertThatThrownBy {
charlieClient.startFlow(::SpendFlow, stateAndRef, aliceParty).returnValue.getOrThrow()
}.isInstanceOf(NotaryException::class.java)
assertThat(firstLatch.await(5, TimeUnit.SECONDS)).isTrue()
firstSubscription.unsubscribe()
assertThat(aliceClient.stateMachinesSnapshot()).isEmpty()
// case 2: the notary exception is caught and wrapped in a custom exception
val secondStateAndRef = charlieClient.startFlow(::IssueFlow, defaultNotaryIdentity).returnValue.get()
charlieClient.startFlow(::SpendFlowWithCustomException, secondStateAndRef, aliceParty).returnValue.get()
val secondSubscription = aliceClient.stateMachinesFeed().updates.subscribe{
if (it is StateMachineUpdate.Removed && it.result.isFailure)
secondLatch.countDown()
}
assertThatThrownBy {
charlieClient.startFlow(::SpendFlowWithCustomException, secondStateAndRef, aliceParty).returnValue.getOrThrow()
}.isInstanceOf(DoubleSpendException::class.java)
assertThat(secondLatch.await(5, TimeUnit.SECONDS)).isTrue()
secondSubscription.unsubscribe()
assertThat(aliceClient.stateMachinesSnapshot()).isEmpty()
}
}
@StartableByRPC
class IssueFlow(val notary: Party): FlowLogic<StateAndRef<SingleOwnerState>>() {
@Suspendable
override fun call(): StateAndRef<SingleOwnerState> {
val partyAndReference = PartyAndReference(ourIdentity, OpaqueBytes.of(1))
val txBuilder = DummyContract.generateInitial(Random().nextInt(), notary, partyAndReference)
val signedTransaction = serviceHub.signInitialTransaction(txBuilder, ourIdentity.owningKey)
val notarised = subFlow(FinalityFlow(signedTransaction, emptySet<FlowSession>()))
return notarised.coreTransaction.outRef(0)
}
}
@StartableByRPC
@InitiatingFlow
class SpendFlow(private val stateAndRef: StateAndRef<SingleOwnerState>, private val newOwner: Party): FlowLogic<Unit>() {
@Suspendable
override fun call() {
val txBuilder = DummyContract.move(stateAndRef, newOwner)
val signedTransaction = serviceHub.signInitialTransaction(txBuilder, ourIdentity.owningKey)
val sessionWithCounterParty = initiateFlow(newOwner)
sessionWithCounterParty.sendAndReceive<String>("initial-message")
subFlow(FinalityFlow(signedTransaction, setOf(sessionWithCounterParty)))
}
}
@InitiatedBy(SpendFlow::class)
class AcceptSpendFlow(private val otherSide: FlowSession): FlowLogic<Unit>() {
@Suspendable
override fun call() {
otherSide.receive<String>()
otherSide.send("initial-response")
subFlow(ReceiveFinalityFlow(otherSide))
}
}
@StartableByRPC
@InitiatingFlow
class SpendFlowWithCustomException(private val stateAndRef: StateAndRef<SingleOwnerState>, private val newOwner: Party):
FlowLogic<Unit>() {
@Suspendable
override fun call() {
val txBuilder = DummyContract.move(stateAndRef, newOwner)
val signedTransaction = serviceHub.signInitialTransaction(txBuilder, ourIdentity.owningKey)
val sessionWithCounterParty = initiateFlow(newOwner)
sessionWithCounterParty.sendAndReceive<String>("initial-message")
try {
subFlow(FinalityFlow(signedTransaction, setOf(sessionWithCounterParty)))
} catch (e: NotaryException) {
throw DoubleSpendException("double spend!", e)
}
}
}
@InitiatedBy(SpendFlowWithCustomException::class)
class AcceptSpendFlowWithCustomException(private val otherSide: FlowSession): FlowLogic<Unit>() {
@Suspendable
override fun call() {
otherSide.receive<String>()
otherSide.send("initial-response")
subFlow(ReceiveFinalityFlow(otherSide))
}
}
class DoubleSpendException(message: String, cause: Throwable): FlowException(message, cause)
}

View File

@ -1,6 +1,7 @@
package net.corda.node.services.config package net.corda.node.services.config
import com.typesafe.config.Config import com.typesafe.config.Config
import com.typesafe.config.ConfigException
import com.typesafe.config.ConfigFactory import com.typesafe.config.ConfigFactory
import com.typesafe.config.ConfigParseOptions import com.typesafe.config.ConfigParseOptions
import net.corda.cliutils.CordaSystemUtils import net.corda.cliutils.CordaSystemUtils
@ -8,6 +9,7 @@ import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.createDirectories import net.corda.core.internal.createDirectories
import net.corda.core.internal.div import net.corda.core.internal.div
import net.corda.core.internal.exists import net.corda.core.internal.exists
import net.corda.node.internal.Node
import net.corda.nodeapi.internal.DEV_CA_KEY_STORE_PASS import net.corda.nodeapi.internal.DEV_CA_KEY_STORE_PASS
import net.corda.nodeapi.internal.config.FileBasedCertificateStoreSupplier import net.corda.nodeapi.internal.config.FileBasedCertificateStoreSupplier
import net.corda.nodeapi.internal.config.MutualSslConfiguration import net.corda.nodeapi.internal.config.MutualSslConfiguration
@ -19,7 +21,10 @@ import net.corda.nodeapi.internal.installDevNodeCaCertPath
import net.corda.nodeapi.internal.loadDevCaTrustStore import net.corda.nodeapi.internal.loadDevCaTrustStore
import net.corda.nodeapi.internal.registerDevP2pCertificates import net.corda.nodeapi.internal.registerDevP2pCertificates
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import java.lang.IllegalStateException
import java.nio.file.Path import java.nio.file.Path
import kotlin.reflect.KClass
import kotlin.reflect.full.memberProperties
fun configOf(vararg pairs: Pair<String, Any?>): Config = ConfigFactory.parseMap(mapOf(*pairs)) fun configOf(vararg pairs: Pair<String, Any?>): Config = ConfigFactory.parseMap(mapOf(*pairs))
operator fun Config.plus(overrides: Map<String, Any?>): Config = ConfigFactory.parseMap(overrides).withFallback(this) operator fun Config.plus(overrides: Map<String, Any?>): Config = ConfigFactory.parseMap(overrides).withFallback(this)
@ -62,9 +67,69 @@ object ConfigHelper {
return finalConfig return finalConfig
} }
private fun <T: Any> getCaseSensitivePropertyPath(target : KClass<T>?, path : List<String>) : String {
require(path.isNotEmpty()) { "Path to config property cannot be empty." }
val lookFor = path.first()
target?.memberProperties?.forEach {
if (it.name.toLowerCase() == lookFor.toLowerCase()) {
return if (path.size > 1)
"${it.name}." +
getCaseSensitivePropertyPath(
(it.getter.returnType.classifier as KClass<*>),
path.subList(1, path.size))
else
it.name
}
}
return ""
}
/*
* Gets
*/
private fun Config.cordaEntriesOnly(): Config { private fun Config.cordaEntriesOnly(): Config {
return ConfigFactory.parseMap(toProperties().filterKeys { (it as String).startsWith(CORDA_PROPERTY_PREFIX) }.mapKeys { (it.key as String).removePrefix(CORDA_PROPERTY_PREFIX) }) val cordaPropOccurrences = mutableSetOf<String>()
val badKeyConversions = mutableSetOf<String>()
return ConfigFactory.parseMap(
toProperties()
.mapKeys {
val newKey = (it.key as String)
.replace("_", ".")
.toLowerCase()
if (newKey.contains(CORDA_PROPERTY_PREFIX) && cordaPropOccurrences.contains(newKey)) {
throw ShadowingException(it.key.toString(), newKey)
}
cordaPropOccurrences.add(newKey)
newKey.let { key ->
if (!key.contains(CORDA_PROPERTY_PREFIX))
return@let key
val nodeConfKey = key.removePrefix(CORDA_PROPERTY_PREFIX)
val configPath = getCaseSensitivePropertyPath(
NodeConfigurationImpl::class,
nodeConfKey.split(".")
)
if (nodeConfKey.length != configPath.length) {
Node.printWarning(
"${it.key} (property or environment variable) cannot be mapped to an existing Corda" +
" config property and thus won't be used as a config override!" +
" It won't be passed as a config override! If that was the intention " +
" double check the spelling and ensure there is such config key.")
badKeyConversions.add(configPath)
}
CORDA_PROPERTY_PREFIX + configPath
}
}.filterKeys { it.startsWith(CORDA_PROPERTY_PREFIX) }
.mapKeys { it.key.removePrefix(CORDA_PROPERTY_PREFIX) }
.filterKeys { !badKeyConversions.contains(it) })
} }
} }

View File

@ -0,0 +1,7 @@
package net.corda.node.services.config
import com.typesafe.config.ConfigException
class ShadowingException(definedProperty : String, convertedProperty : String)
: ConfigException(
"Environment variable $definedProperty is shadowing another property transformed to $convertedProperty")

View File

@ -71,7 +71,7 @@ sealed class Event {
* Initiate a flow. This causes a new session object to be created and returned to the flow. Note that no actual * Initiate a flow. This causes a new session object to be created and returned to the flow. Note that no actual
* communication takes place at this time, only on the first send/receive operation on the session. * communication takes place at this time, only on the first send/receive operation on the session.
*/ */
data class InitiateFlow(val destination: Destination) : Event() data class InitiateFlow(val destination: Destination, val wellKnownParty: Party) : Event()
/** /**
* Signal the entering into a subflow. * Signal the entering into a subflow.

View File

@ -17,10 +17,11 @@ import net.corda.core.utilities.UntrustworthyData
class FlowSessionImpl( class FlowSessionImpl(
override val destination: Destination, override val destination: Destination,
private val wellKnownParty: Party,
val sourceSessionId: SessionId val sourceSessionId: SessionId
) : FlowSession() { ) : FlowSession() {
override val counterparty: Party get() = checkNotNull(destination as? Party) { "$destination is not a Party" } override val counterparty: Party get() = wellKnownParty
override fun toString(): String = "FlowSessionImpl(destination=$destination, sourceSessionId=$sourceSessionId)" override fun toString(): String = "FlowSessionImpl(destination=$destination, sourceSessionId=$sourceSessionId)"

View File

@ -355,10 +355,10 @@ class FlowStateMachineImpl<R>(override val id: StateMachineRunId,
} }
@Suspendable @Suspendable
override fun initiateFlow(destination: Destination): FlowSession { override fun initiateFlow(destination: Destination, wellKnownParty: Party): FlowSession {
require(destination is Party || destination is AnonymousParty) { "Unsupported destination type ${destination.javaClass.name}" } require(destination is Party || destination is AnonymousParty) { "Unsupported destination type ${destination.javaClass.name}" }
val resume = processEventImmediately( val resume = processEventImmediately(
Event.InitiateFlow(destination), Event.InitiateFlow(destination, wellKnownParty),
isDbTransactionOpenOnEntry = true, isDbTransactionOpenOnEntry = true,
isDbTransactionOpenOnExit = true isDbTransactionOpenOnExit = true
) as FlowContinuation.Resume ) as FlowContinuation.Resume

View File

@ -466,7 +466,7 @@ class SingleThreadedStateMachineManager(
try { try {
val initiatedFlowFactory = getInitiatedFlowFactory(sessionMessage) val initiatedFlowFactory = getInitiatedFlowFactory(sessionMessage)
val initiatedSessionId = SessionId.createRandom(secureRandom) val initiatedSessionId = SessionId.createRandom(secureRandom)
val senderSession = FlowSessionImpl(sender, initiatedSessionId) val senderSession = FlowSessionImpl(sender, sender, initiatedSessionId)
val flowLogic = initiatedFlowFactory.createFlow(senderSession) val flowLogic = initiatedFlowFactory.createFlow(senderSession)
val initiatedFlowInfo = when (initiatedFlowFactory) { val initiatedFlowInfo = when (initiatedFlowFactory) {
is InitiatedFlowFactory.Core -> FlowInfo(serviceHub.myInfo.platformVersion, "corda") is InitiatedFlowFactory.Core -> FlowInfo(serviceHub.myInfo.platformVersion, "corda")

View File

@ -1,9 +1,13 @@
package net.corda.node.services.statemachine package net.corda.node.services.statemachine
import net.corda.core.crypto.newSecureRandom import net.corda.core.crypto.newSecureRandom
import net.corda.core.flows.FlowException
import net.corda.core.flows.ReceiveFinalityFlow import net.corda.core.flows.ReceiveFinalityFlow
import net.corda.core.flows.ReceiveTransactionFlow
import net.corda.core.flows.StateMachineRunId import net.corda.core.flows.StateMachineRunId
import net.corda.core.flows.UnexpectedFlowEndException
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.internal.DeclaredField
import net.corda.core.internal.ThreadBox import net.corda.core.internal.ThreadBox
import net.corda.core.internal.TimedFlow import net.corda.core.internal.TimedFlow
import net.corda.core.internal.bufferUntilSubscribed import net.corda.core.internal.bufferUntilSubscribed
@ -307,10 +311,19 @@ class StaffedFlowHospital(private val flowMessaging: FlowMessaging, private val
object FinalityDoctor : Staff { object FinalityDoctor : Staff {
override fun consult(flowFiber: FlowFiber, currentState: StateMachineState, newError: Throwable, history: FlowMedicalHistory): Diagnosis { override fun consult(flowFiber: FlowFiber, currentState: StateMachineState, newError: Throwable, history: FlowMedicalHistory): Diagnosis {
return if (currentState.flowLogic is FinalityHandler || isFromReceiveFinalityFlow(newError)) { return if (currentState.flowLogic is FinalityHandler) {
log.warn("Flow ${flowFiber.id} failed to be finalised. Manual intervention may be required before retrying " + log.warn("Flow ${flowFiber.id} failed to be finalised. Manual intervention may be required before retrying " +
"the flow by re-starting the node. State machine state: $currentState", newError) "the flow by re-starting the node. State machine state: $currentState", newError)
Diagnosis.OVERNIGHT_OBSERVATION Diagnosis.OVERNIGHT_OBSERVATION
} else if (isFromReceiveFinalityFlow(newError)) {
if (isErrorPropagatedFromCounterparty(newError) && isErrorThrownDuringReceiveFinality(newError)) {
// no need to keep around the flow, since notarisation has already failed at the counterparty.
Diagnosis.NOT_MY_SPECIALTY
} else {
log.warn("Flow ${flowFiber.id} failed to be finalised. Manual intervention may be required before retrying " +
"the flow by re-starting the node. State machine state: $currentState", newError)
Diagnosis.OVERNIGHT_OBSERVATION
}
} else { } else {
Diagnosis.NOT_MY_SPECIALTY Diagnosis.NOT_MY_SPECIALTY
} }
@ -319,6 +332,36 @@ class StaffedFlowHospital(private val flowMessaging: FlowMessaging, private val
private fun isFromReceiveFinalityFlow(throwable: Throwable): Boolean { private fun isFromReceiveFinalityFlow(throwable: Throwable): Boolean {
return throwable.stackTrace.any { it.className == ReceiveFinalityFlow::class.java.name } return throwable.stackTrace.any { it.className == ReceiveFinalityFlow::class.java.name }
} }
private fun isErrorPropagatedFromCounterparty(error: Throwable): Boolean {
return when(error) {
is UnexpectedFlowEndException -> {
val peer = DeclaredField<Party?>(UnexpectedFlowEndException::class.java, "peer", error).value
peer != null
}
is FlowException -> {
val peer = DeclaredField<Party?>(FlowException::class.java, "peer", error).value
peer != null
}
else -> false
}
}
/**
* This method will return true if [ReceiveTransactionFlow] is at the top of the stack during the error.
* As a result, if the failure happened during a sub-flow invoked from [ReceiveTransactionFlow], the method will return false.
*
* This is because in the latter case, the transaction might have already been finalised and deleting the flow
* would introduce risk for inconsistency between nodes.
*/
private fun isErrorThrownDuringReceiveFinality(error: Throwable): Boolean {
val strippedStacktrace = error.stackTrace
.filterNot { it?.className?.contains("counter-flow exception from peer") ?: false }
.filterNot { it?.className?.startsWith("net.corda.node.services.statemachine.") ?: false }
return strippedStacktrace.isNotEmpty() &&
strippedStacktrace.first().className.startsWith(ReceiveTransactionFlow::class.qualifiedName!! )
}
} }
/** /**

View File

@ -235,7 +235,7 @@ class TopLevelTransition(
return@builder FlowContinuation.ProcessEvents return@builder FlowContinuation.ProcessEvents
} }
val sourceSessionId = SessionId.createRandom(context.secureRandom) val sourceSessionId = SessionId.createRandom(context.secureRandom)
val sessionImpl = FlowSessionImpl(event.destination, sourceSessionId) val sessionImpl = FlowSessionImpl(event.destination, event.wellKnownParty, sourceSessionId)
val newSessions = checkpoint.sessions + (sourceSessionId to SessionState.Uninitiated(event.destination, initiatingSubFlow, sourceSessionId, context.secureRandom.nextLong())) val newSessions = checkpoint.sessions + (sourceSessionId to SessionState.Uninitiated(event.destination, initiatingSubFlow, sourceSessionId, context.secureRandom.nextLong()))
currentState = currentState.copy(checkpoint = checkpoint.copy(sessions = newSessions)) currentState = currentState.copy(checkpoint = checkpoint.copy(sessions = newSessions))
actions.add(Action.AddSessionBinding(context.id, sourceSessionId)) actions.add(Action.AddSessionBinding(context.id, sourceSessionId))

View File

@ -69,8 +69,8 @@ class FlowFrameworkTests {
@Before @Before
fun setUpMockNet() { fun setUpMockNet() {
mockNet = InternalMockNetwork( mockNet = InternalMockNetwork(
cordappsForAllNodes = listOf(DUMMY_CONTRACTS_CORDAPP), cordappsForAllNodes = listOf(DUMMY_CONTRACTS_CORDAPP),
servicePeerAllocationStrategy = RoundRobin() servicePeerAllocationStrategy = RoundRobin()
) )
aliceNode = mockNet.createNode(InternalMockNodeParameters(legalName = ALICE_NAME)) aliceNode = mockNet.createNode(InternalMockNodeParameters(legalName = ALICE_NAME))
@ -139,13 +139,13 @@ class FlowFrameworkTests {
mockNet.runNetwork() mockNet.runNetwork()
assertSessionTransfers( assertSessionTransfers(
aliceNode sent sessionInit(PingPongFlow::class, payload = 10L) to bobNode, aliceNode sent sessionInit(PingPongFlow::class, payload = 10L) to bobNode,
bobNode sent sessionConfirm() to aliceNode, bobNode sent sessionConfirm() to aliceNode,
bobNode sent sessionData(20L) to aliceNode, bobNode sent sessionData(20L) to aliceNode,
aliceNode sent sessionData(11L) to bobNode, aliceNode sent sessionData(11L) to bobNode,
bobNode sent sessionData(21L) to aliceNode, bobNode sent sessionData(21L) to aliceNode,
aliceNode sent normalEnd to bobNode, aliceNode sent normalEnd to bobNode,
bobNode sent normalEnd to aliceNode bobNode sent normalEnd to aliceNode
) )
} }
@ -167,7 +167,8 @@ class FlowFrameworkTests {
it.message is ExistingSessionMessage && it.message.payload === EndSessionMessage it.message is ExistingSessionMessage && it.message.payload === EndSessionMessage
}.subscribe { sessionEndReceived.release() } }.subscribe { sessionEndReceived.release() }
val resultFuture = aliceNode.services.startFlow( val resultFuture = aliceNode.services.startFlow(
WaitForOtherSideEndBeforeSendAndReceive(bob, sessionEndReceived)).resultFuture WaitForOtherSideEndBeforeSendAndReceive(bob, sessionEndReceived)
).resultFuture
mockNet.runNetwork() mockNet.runNetwork()
assertThatExceptionOfType(UnexpectedFlowEndException::class.java).isThrownBy { assertThatExceptionOfType(UnexpectedFlowEndException::class.java).isThrownBy {
resultFuture.getOrThrow() resultFuture.getOrThrow()
@ -186,10 +187,10 @@ class FlowFrameworkTests {
mockNet.runNetwork() mockNet.runNetwork()
assertThatExceptionOfType(MyFlowException::class.java) assertThatExceptionOfType(MyFlowException::class.java)
.isThrownBy { receivingFiber.resultFuture.getOrThrow() } .isThrownBy { receivingFiber.resultFuture.getOrThrow() }
.withMessage("Nothing useful") .withMessage("Nothing useful")
.withStackTraceContaining(ReceiveFlow::class.java.name) // Make sure the stack trace is that of the receiving flow .withStackTraceContaining(ReceiveFlow::class.java.name) // Make sure the stack trace is that of the receiving flow
.withStackTraceContaining("Received counter-flow exception from peer") .withStackTraceContaining("Received counter-flow exception from peer")
bobNode.database.transaction { bobNode.database.transaction {
assertThat(bobNode.internals.checkpointStorage.checkpoints()).isEmpty() assertThat(bobNode.internals.checkpointStorage.checkpoints()).isEmpty()
} }
@ -197,15 +198,15 @@ class FlowFrameworkTests {
assertThat(receivingFiber.state).isEqualTo(Strand.State.WAITING) assertThat(receivingFiber.state).isEqualTo(Strand.State.WAITING)
assertThat((erroringFlow.get().stateMachine as FlowStateMachineImpl).state).isEqualTo(Strand.State.WAITING) assertThat((erroringFlow.get().stateMachine as FlowStateMachineImpl).state).isEqualTo(Strand.State.WAITING)
assertThat(erroringFlowSteps.get()).containsExactly( assertThat(erroringFlowSteps.get()).containsExactly(
Notification.createOnNext(ProgressTracker.STARTING), Notification.createOnNext(ProgressTracker.STARTING),
Notification.createOnNext(ExceptionFlow.START_STEP), Notification.createOnNext(ExceptionFlow.START_STEP),
Notification.createOnError(erroringFlow.get().exceptionThrown) Notification.createOnError(erroringFlow.get().exceptionThrown)
) )
assertSessionTransfers( assertSessionTransfers(
aliceNode sent sessionInit(ReceiveFlow::class) to bobNode, aliceNode sent sessionInit(ReceiveFlow::class) to bobNode,
bobNode sent sessionConfirm() to aliceNode, bobNode sent sessionConfirm() to aliceNode,
bobNode sent errorMessage(erroringFlow.get().exceptionThrown) to aliceNode bobNode sent errorMessage(erroringFlow.get().exceptionThrown) to aliceNode
) )
// Make sure the original stack trace isn't sent down the wire // Make sure the original stack trace isn't sent down the wire
val lastMessage = receivedSessionMessages.last().message as ExistingSessionMessage val lastMessage = receivedSessionMessages.last().message as ExistingSessionMessage
@ -296,8 +297,8 @@ class FlowFrameworkTests {
@Test @Test
fun waitForLedgerCommit() { fun waitForLedgerCommit() {
val ptx = TransactionBuilder(notary = notaryIdentity) val ptx = TransactionBuilder(notary = notaryIdentity)
.addOutputState(DummyState(), DummyContract.PROGRAM_ID) .addOutputState(DummyState(), DummyContract.PROGRAM_ID)
.addCommand(dummyCommand(alice.owningKey)) .addCommand(dummyCommand(alice.owningKey))
val stx = aliceNode.services.signInitialTransaction(ptx) val stx = aliceNode.services.signInitialTransaction(ptx)
val committerStx = aliceNode.registerCordappFlowFactory(CommitterFlow::class) { val committerStx = aliceNode.registerCordappFlowFactory(CommitterFlow::class) {
@ -313,8 +314,8 @@ class FlowFrameworkTests {
@Test @Test
fun `waitForLedgerCommit throws exception if any active session ends in error`() { fun `waitForLedgerCommit throws exception if any active session ends in error`() {
val ptx = TransactionBuilder(notary = notaryIdentity) val ptx = TransactionBuilder(notary = notaryIdentity)
.addOutputState(DummyState(), DummyContract.PROGRAM_ID) .addOutputState(DummyState(), DummyContract.PROGRAM_ID)
.addCommand(dummyCommand()) .addCommand(dummyCommand())
val stx = aliceNode.services.signInitialTransaction(ptx) val stx = aliceNode.services.signInitialTransaction(ptx)
aliceNode.registerCordappFlowFactory(WaitForLedgerCommitFlow::class) { ExceptionFlow { throw Exception("Error") } } aliceNode.registerCordappFlowFactory(WaitForLedgerCommitFlow::class) { ExceptionFlow { throw Exception("Error") } }
@ -354,8 +355,8 @@ class FlowFrameworkTests {
val result = aliceNode.services.startFlow(UpgradedFlow(bob)).resultFuture val result = aliceNode.services.startFlow(UpgradedFlow(bob)).resultFuture
mockNet.runNetwork() mockNet.runNetwork()
assertThat(receivedSessionMessages).startsWith( assertThat(receivedSessionMessages).startsWith(
aliceNode sent sessionInit(UpgradedFlow::class, flowVersion = 2) to bobNode, aliceNode sent sessionInit(UpgradedFlow::class, flowVersion = 2) to bobNode,
bobNode sent sessionConfirm(flowVersion = 1) to aliceNode bobNode sent sessionConfirm(flowVersion = 1) to aliceNode
) )
val (receivedPayload, node2FlowVersion) = result.getOrThrow() val (receivedPayload, node2FlowVersion) = result.getOrThrow()
assertThat(receivedPayload).isEqualTo("Old initiated") assertThat(receivedPayload).isEqualTo("Old initiated")
@ -369,8 +370,8 @@ class FlowFrameworkTests {
val flowInfo = aliceNode.services.startFlow(initiatingFlow).resultFuture val flowInfo = aliceNode.services.startFlow(initiatingFlow).resultFuture
mockNet.runNetwork() mockNet.runNetwork()
assertThat(receivedSessionMessages).startsWith( assertThat(receivedSessionMessages).startsWith(
aliceNode sent sessionInit(SendFlow::class, flowVersion = 1, payload = "Old initiating") to bobNode, aliceNode sent sessionInit(SendFlow::class, flowVersion = 1, payload = "Old initiating") to bobNode,
bobNode sent sessionConfirm(flowVersion = 2) to aliceNode bobNode sent sessionConfirm(flowVersion = 2) to aliceNode
) )
assertThat(flowInfo.get().flowVersion).isEqualTo(2) assertThat(flowInfo.get().flowVersion).isEqualTo(2)
} }
@ -380,8 +381,8 @@ class FlowFrameworkTests {
val future = aliceNode.services.startFlow(NeverRegisteredFlow("Hello", bob)).resultFuture val future = aliceNode.services.startFlow(NeverRegisteredFlow("Hello", bob)).resultFuture
mockNet.runNetwork() mockNet.runNetwork()
assertThatExceptionOfType(UnexpectedFlowEndException::class.java) assertThatExceptionOfType(UnexpectedFlowEndException::class.java)
.isThrownBy { future.getOrThrow() } .isThrownBy { future.getOrThrow() }
.withMessageEndingWith("${NeverRegisteredFlow::class.java.name} is not registered") .withMessageEndingWith("${NeverRegisteredFlow::class.java.name} is not registered")
} }
@Test @Test
@ -441,9 +442,9 @@ class FlowFrameworkTests {
erroringFlowFuture.getOrThrow() erroringFlowFuture.getOrThrow()
val flowSteps = erroringFlowSteps.get() val flowSteps = erroringFlowSteps.get()
assertThat(flowSteps).containsExactly( assertThat(flowSteps).containsExactly(
Notification.createOnNext(ProgressTracker.STARTING), Notification.createOnNext(ProgressTracker.STARTING),
Notification.createOnNext(ExceptionFlow.START_STEP), Notification.createOnNext(ExceptionFlow.START_STEP),
Notification.createOnError(erroringFlowFuture.get().exceptionThrown) Notification.createOnError(erroringFlowFuture.get().exceptionThrown)
) )
val receiveFlowException = assertFailsWith(UnexpectedFlowEndException::class) { val receiveFlowException = assertFailsWith(UnexpectedFlowEndException::class) {
@ -451,28 +452,28 @@ class FlowFrameworkTests {
} }
assertThat(receiveFlowException.message).doesNotContain("evil bug!") assertThat(receiveFlowException.message).doesNotContain("evil bug!")
assertThat(receiveFlowSteps.get()).containsExactly( assertThat(receiveFlowSteps.get()).containsExactly(
Notification.createOnNext(ProgressTracker.STARTING), Notification.createOnNext(ProgressTracker.STARTING),
Notification.createOnNext(ReceiveFlow.START_STEP), Notification.createOnNext(ReceiveFlow.START_STEP),
Notification.createOnError(receiveFlowException) Notification.createOnError(receiveFlowException)
) )
assertSessionTransfers( assertSessionTransfers(
aliceNode sent sessionInit(ReceiveFlow::class) to bobNode, aliceNode sent sessionInit(ReceiveFlow::class) to bobNode,
bobNode sent sessionConfirm() to aliceNode, bobNode sent sessionConfirm() to aliceNode,
bobNode sent errorMessage() to aliceNode bobNode sent errorMessage() to aliceNode
) )
} }
@Test @Test
fun `initiating flow using unknown AnonymousParty`() { fun `initiating flow using unknown AnonymousParty`() {
val anonymousBob = bobNode.services.keyManagementService.freshKeyAndCert(bobNode.info.legalIdentitiesAndCerts.single(), false) val anonymousBob = bobNode.services.keyManagementService.freshKeyAndCert(bobNode.info.legalIdentitiesAndCerts.single(), false)
.party.anonymise() .party.anonymise()
bobNode.registerCordappFlowFactory(SendAndReceiveFlow::class) { SingleInlinedSubFlow(it) } bobNode.registerCordappFlowFactory(SendAndReceiveFlow::class) { SingleInlinedSubFlow(it) }
val result = aliceNode.services.startFlow(SendAndReceiveFlow(anonymousBob, "Hello")).resultFuture val result = aliceNode.services.startFlow(SendAndReceiveFlow(anonymousBob, "Hello")).resultFuture
mockNet.runNetwork() mockNet.runNetwork()
assertThatIllegalArgumentException() assertThatIllegalArgumentException()
.isThrownBy { result.getOrThrow() } .isThrownBy { result.getOrThrow() }
.withMessage("We do not know who $anonymousBob belongs to") .withMessage("Could not resolve destination: $anonymousBob")
} }
@Test @Test
@ -497,16 +498,18 @@ class FlowFrameworkTests {
private val FlowLogic<*>.progressSteps: CordaFuture<List<Notification<ProgressTracker.Step>>> private val FlowLogic<*>.progressSteps: CordaFuture<List<Notification<ProgressTracker.Step>>>
get() { get() {
return progressTracker!!.changes return progressTracker!!.changes
.ofType(Change.Position::class.java) .ofType(Change.Position::class.java)
.map { it.newStep } .map { it.newStep }
.materialize() .materialize()
.toList() .toList()
.toFuture() .toFuture()
} }
@InitiatingFlow @InitiatingFlow
private class WaitForOtherSideEndBeforeSendAndReceive(val otherParty: Party, private class WaitForOtherSideEndBeforeSendAndReceive(
@Transient val receivedOtherFlowEnd: Semaphore) : FlowLogic<Unit>() { val otherParty: Party,
@Transient val receivedOtherFlowEnd: Semaphore
) : FlowLogic<Unit>() {
@Suspendable @Suspendable
override fun call() { override fun call() {
// Kick off the flow on the other side ... // Kick off the flow on the other side ...
@ -626,7 +629,8 @@ class FlowFrameworkTests {
//endregion Helpers //endregion Helpers
} }
internal fun sessionConfirm(flowVersion: Int = 1) = ExistingSessionMessage(SessionId(0), ConfirmSessionMessage(SessionId(0), FlowInfo(flowVersion, ""))) internal fun sessionConfirm(flowVersion: Int = 1) =
ExistingSessionMessage(SessionId(0), ConfirmSessionMessage(SessionId(0), FlowInfo(flowVersion, "")))
internal inline fun <reified P : FlowLogic<*>> TestStartedNode.getSingleFlow(): Pair<P, CordaFuture<*>> { internal inline fun <reified P : FlowLogic<*>> TestStartedNode.getSingleFlow(): Pair<P, CordaFuture<*>> {
return smm.findStateMachines(P::class.java).single() return smm.findStateMachines(P::class.java).single()
@ -637,17 +641,17 @@ private fun sanitise(message: SessionMessage) = when (message) {
is ExistingSessionMessage -> { is ExistingSessionMessage -> {
val payload = message.payload val payload = message.payload
message.copy( message.copy(
recipientSessionId = SessionId(0), recipientSessionId = SessionId(0),
payload = when (payload) { payload = when (payload) {
is ConfirmSessionMessage -> payload.copy( is ConfirmSessionMessage -> payload.copy(
initiatedSessionId = SessionId(0), initiatedSessionId = SessionId(0),
initiatedFlowInfo = payload.initiatedFlowInfo.copy(appName = "") initiatedFlowInfo = payload.initiatedFlowInfo.copy(appName = "")
) )
is ErrorSessionMessage -> payload.copy( is ErrorSessionMessage -> payload.copy(
errorId = 0 errorId = 0
) )
else -> payload else -> payload
} }
) )
} }
} }
@ -667,10 +671,12 @@ internal fun TestStartedNode.sendSessionMessage(message: SessionMessage, destina
} }
} }
internal fun errorMessage(errorResponse: FlowException? = null) = ExistingSessionMessage(SessionId(0), ErrorSessionMessage(errorResponse, 0)) internal fun errorMessage(errorResponse: FlowException? = null) =
ExistingSessionMessage(SessionId(0), ErrorSessionMessage(errorResponse, 0))
internal infix fun TestStartedNode.sent(message: SessionMessage): Pair<Int, SessionMessage> = Pair(internals.id, message) internal infix fun TestStartedNode.sent(message: SessionMessage): Pair<Int, SessionMessage> = Pair(internals.id, message)
internal infix fun Pair<Int, SessionMessage>.to(node: TestStartedNode): SessionTransfer = SessionTransfer(first, second, node.network.myAddress) internal infix fun Pair<Int, SessionMessage>.to(node: TestStartedNode): SessionTransfer =
SessionTransfer(first, second, node.network.myAddress)
internal data class SessionTransfer(val from: Int, val message: SessionMessage, val to: MessageRecipients) { internal data class SessionTransfer(val from: Int, val message: SessionMessage, val to: MessageRecipients) {
val isPayloadTransfer: Boolean val isPayloadTransfer: Boolean
@ -785,7 +791,11 @@ internal class MyFlowException(override val message: String) : FlowException() {
internal class MyPeerFlowException(override val message: String, val peer: Party) : FlowException() internal class MyPeerFlowException(override val message: String, val peer: Party) : FlowException()
@InitiatingFlow @InitiatingFlow
internal class SendAndReceiveFlow(private val destination: Destination, private val payload: Any, private val otherPartySession: FlowSession? = null) : FlowLogic<Any>() { internal class SendAndReceiveFlow(
private val destination: Destination,
private val payload: Any,
private val otherPartySession: FlowSession? = null
) : FlowLogic<Any>() {
constructor(otherPartySession: FlowSession, payload: Any) : this(otherPartySession.counterparty, payload, otherPartySession) constructor(otherPartySession: FlowSession, payload: Any) : this(otherPartySession.counterparty, payload, otherPartySession)
@Suspendable @Suspendable
@ -795,7 +805,8 @@ internal class SendAndReceiveFlow(private val destination: Destination, private
} }
@InitiatingFlow @InitiatingFlow
internal class PingPongFlow(private val otherParty: Party, private val payload: Long, private val otherPartySession: FlowSession? = null) : FlowLogic<Unit>() { internal class PingPongFlow(private val otherParty: Party, private val payload: Long, private val otherPartySession: FlowSession? = null) :
FlowLogic<Unit>() {
constructor(otherPartySession: FlowSession, payload: Long) : this(otherPartySession.counterparty, payload, otherPartySession) constructor(otherPartySession: FlowSession, payload: Long) : this(otherPartySession.counterparty, payload, otherPartySession)
@Transient @Transient

View File

@ -6,7 +6,8 @@ import net.corda.core.DeleteForDJVM
import net.corda.core.KeepForDJVM import net.corda.core.KeepForDJVM
import net.corda.core.StubOutForDJVM import net.corda.core.StubOutForDJVM
import net.corda.core.cordapp.Cordapp import net.corda.core.cordapp.Cordapp
import net.corda.core.internal.* import net.corda.core.internal.toSynchronised
import net.corda.core.internal.uncheckedCast
import net.corda.core.serialization.* import net.corda.core.serialization.*
import net.corda.core.utilities.ByteSequence import net.corda.core.utilities.ByteSequence
import net.corda.serialization.internal.CordaSerializationMagic import net.corda.serialization.internal.CordaSerializationMagic
@ -46,14 +47,20 @@ abstract class AbstractAMQPSerializationScheme(
val sff: SerializerFactoryFactory = createSerializerFactoryFactory() val sff: SerializerFactoryFactory = createSerializerFactoryFactory()
) : SerializationScheme { ) : SerializationScheme {
@DeleteForDJVM @DeleteForDJVM
constructor(cordapps: List<Cordapp>) : this(cordapps.customSerializers, cordapps.serializationWhitelists, AccessOrderLinkedHashMap<SerializationFactoryCacheKey, SerializerFactory>(128).toSynchronised()) constructor(cordapps: List<Cordapp>) : this(
cordapps.customSerializers,
cordapps.serializationWhitelists,
AccessOrderLinkedHashMap<SerializationFactoryCacheKey, SerializerFactory>(128).toSynchronised()
)
// This is a bit gross but a broader check for ConcurrentMap is not allowed inside DJVM. // This is a bit gross but a broader check for ConcurrentMap is not allowed inside DJVM.
private val serializerFactoriesForContexts: MutableMap<SerializationFactoryCacheKey, SerializerFactory> = if (maybeNotConcurrentSerializerFactoriesForContexts is AccessOrderLinkedHashMap<*, *>) { private val serializerFactoriesForContexts: MutableMap<SerializationFactoryCacheKey, SerializerFactory> =
Collections.synchronizedMap(maybeNotConcurrentSerializerFactoriesForContexts) if (maybeNotConcurrentSerializerFactoriesForContexts is
} else { AccessOrderLinkedHashMap<SerializationFactoryCacheKey, SerializerFactory>) {
maybeNotConcurrentSerializerFactoriesForContexts Collections.synchronizedMap(maybeNotConcurrentSerializerFactoriesForContexts)
} } else {
maybeNotConcurrentSerializerFactoriesForContexts
}
companion object { companion object {
private val serializationWhitelists: List<SerializationWhitelist> by lazy { listOf(DefaultWhitelist) } private val serializationWhitelists: List<SerializationWhitelist> by lazy { listOf(DefaultWhitelist) }
@ -68,45 +75,16 @@ abstract class AbstractAMQPSerializationScheme(
} }
private fun registerCustomSerializers(context: SerializationContext, factory: SerializerFactory) { private fun registerCustomSerializers(context: SerializationContext, factory: SerializerFactory) {
with(factory) { factory.register(publicKeySerializer)
register(publicKeySerializer) registerCustomSerializers(factory)
register(net.corda.serialization.internal.amqp.custom.PrivateKeySerializer)
register(net.corda.serialization.internal.amqp.custom.ThrowableSerializer(this))
register(net.corda.serialization.internal.amqp.custom.BigDecimalSerializer)
register(net.corda.serialization.internal.amqp.custom.BigIntegerSerializer)
register(net.corda.serialization.internal.amqp.custom.CurrencySerializer)
register(net.corda.serialization.internal.amqp.custom.OpaqueBytesSubSequenceSerializer(this))
register(net.corda.serialization.internal.amqp.custom.InstantSerializer(this))
register(net.corda.serialization.internal.amqp.custom.DurationSerializer(this))
register(net.corda.serialization.internal.amqp.custom.LocalDateSerializer(this))
register(net.corda.serialization.internal.amqp.custom.LocalDateTimeSerializer(this))
register(net.corda.serialization.internal.amqp.custom.LocalTimeSerializer(this))
register(net.corda.serialization.internal.amqp.custom.ZonedDateTimeSerializer(this))
register(net.corda.serialization.internal.amqp.custom.ZoneIdSerializer(this))
register(net.corda.serialization.internal.amqp.custom.OffsetTimeSerializer(this))
register(net.corda.serialization.internal.amqp.custom.OffsetDateTimeSerializer(this))
register(net.corda.serialization.internal.amqp.custom.OptionalSerializer(this))
register(net.corda.serialization.internal.amqp.custom.YearSerializer(this))
register(net.corda.serialization.internal.amqp.custom.YearMonthSerializer(this))
register(net.corda.serialization.internal.amqp.custom.MonthDaySerializer(this))
register(net.corda.serialization.internal.amqp.custom.PeriodSerializer(this))
register(net.corda.serialization.internal.amqp.custom.ClassSerializer(this))
register(net.corda.serialization.internal.amqp.custom.X509CertificateSerializer)
register(net.corda.serialization.internal.amqp.custom.X509CRLSerializer)
register(net.corda.serialization.internal.amqp.custom.CertPathSerializer(this))
register(net.corda.serialization.internal.amqp.custom.StringBufferSerializer)
register(net.corda.serialization.internal.amqp.custom.InputStreamSerializer)
register(net.corda.serialization.internal.amqp.custom.BitSetSerializer(this))
register(net.corda.serialization.internal.amqp.custom.EnumSetSerializer(this))
register(net.corda.serialization.internal.amqp.custom.ContractAttachmentSerializer(this))
registerNonDeterministicSerializers(factory)
}
// This step is registering custom serializers, which have been added after node initialisation (i.e. via attachments during transaction verification). // This step is registering custom serializers, which have been added after node initialisation (i.e. via attachments during
// Note: the order between the registration of customSerializers and cordappCustomSerializers must be preserved as-is. The reason is the following: // transaction verification).
// Currently, the serialization infrastructure does not support multiple versions of a class (the first one that is registered dominates). // Note: the order between the registration of customSerializers and cordappCustomSerializers must be preserved as-is. The reason
// As a result, when inside a context with attachments class loader, we prioritize serializers loaded on-demand from attachments to serializers that had been // is the following:
// loaded during node initialisation, by scanning the cordapps folder. // Currently, the serialization infrastructure does not support multiple versions of a class (the first one that is
// registered dominates). As a result, when inside a context with attachments class loader, we prioritize serializers loaded
// on-demand from attachments to serializers that had been loaded during node initialisation, by scanning the cordapps folder.
context.customSerializers.forEach { customSerializer -> context.customSerializers.forEach { customSerializer ->
factory.registerExternal(CorDappCustomSerializer(customSerializer, factory)) factory.registerExternal(CorDappCustomSerializer(customSerializer, factory))
} }
@ -126,22 +104,12 @@ abstract class AbstractAMQPSerializationScheme(
factory.addToWhitelist(*it.whitelist.toTypedArray()) factory.addToWhitelist(*it.whitelist.toTypedArray())
} }
cordappSerializationWhitelists.forEach { cordappSerializationWhitelists.forEach {
it.whitelist.forEach { it.whitelist.forEach { clazz ->
clazz -> factory.addToWhitelist(clazz) factory.addToWhitelist(clazz)
} }
} }
} }
/*
* Register the serializers which will be excluded from the DJVM.
*/
@StubOutForDJVM
private fun registerNonDeterministicSerializers(factory: SerializerFactory) {
with(factory) {
register(net.corda.serialization.internal.amqp.custom.SimpleStringSerializer)
}
}
protected abstract fun rpcClientSerializerFactory(context: SerializationContext): SerializerFactory protected abstract fun rpcClientSerializerFactory(context: SerializationContext): SerializerFactory
protected abstract fun rpcServerSerializerFactory(context: SerializationContext): SerializerFactory protected abstract fun rpcServerSerializerFactory(context: SerializationContext): SerializerFactory
@ -189,3 +157,49 @@ abstract class AbstractAMQPSerializationScheme(
protected fun canDeserializeVersion(magic: CordaSerializationMagic) = magic == amqpMagic protected fun canDeserializeVersion(magic: CordaSerializationMagic) = magic == amqpMagic
} }
fun registerCustomSerializers(factory: SerializerFactory) {
with(factory) {
register(net.corda.serialization.internal.amqp.custom.PrivateKeySerializer)
register(net.corda.serialization.internal.amqp.custom.ThrowableSerializer(this))
register(net.corda.serialization.internal.amqp.custom.BigDecimalSerializer)
register(net.corda.serialization.internal.amqp.custom.BigIntegerSerializer)
register(net.corda.serialization.internal.amqp.custom.CurrencySerializer)
register(net.corda.serialization.internal.amqp.custom.OpaqueBytesSubSequenceSerializer(this))
register(net.corda.serialization.internal.amqp.custom.InstantSerializer(this))
register(net.corda.serialization.internal.amqp.custom.DurationSerializer(this))
register(net.corda.serialization.internal.amqp.custom.LocalDateSerializer(this))
register(net.corda.serialization.internal.amqp.custom.LocalDateTimeSerializer(this))
register(net.corda.serialization.internal.amqp.custom.LocalTimeSerializer(this))
register(net.corda.serialization.internal.amqp.custom.ZonedDateTimeSerializer(this))
register(net.corda.serialization.internal.amqp.custom.ZoneIdSerializer(this))
register(net.corda.serialization.internal.amqp.custom.OffsetTimeSerializer(this))
register(net.corda.serialization.internal.amqp.custom.OffsetDateTimeSerializer(this))
register(net.corda.serialization.internal.amqp.custom.OptionalSerializer(this))
register(net.corda.serialization.internal.amqp.custom.YearSerializer(this))
register(net.corda.serialization.internal.amqp.custom.YearMonthSerializer(this))
register(net.corda.serialization.internal.amqp.custom.MonthDaySerializer(this))
register(net.corda.serialization.internal.amqp.custom.PeriodSerializer(this))
register(net.corda.serialization.internal.amqp.custom.ClassSerializer(this))
register(net.corda.serialization.internal.amqp.custom.X509CertificateSerializer)
register(net.corda.serialization.internal.amqp.custom.X509CRLSerializer)
register(net.corda.serialization.internal.amqp.custom.CertPathSerializer(this))
register(net.corda.serialization.internal.amqp.custom.StringBufferSerializer)
register(net.corda.serialization.internal.amqp.custom.InputStreamSerializer)
register(net.corda.serialization.internal.amqp.custom.BitSetSerializer(this))
register(net.corda.serialization.internal.amqp.custom.EnumSetSerializer(this))
register(net.corda.serialization.internal.amqp.custom.ContractAttachmentSerializer(this))
}
registerNonDeterministicSerializers(factory)
}
/*
* Register the serializers which will be excluded from the DJVM.
*/
@StubOutForDJVM
private fun registerNonDeterministicSerializers(factory: SerializerFactory) {
with(factory) {
register(net.corda.serialization.internal.amqp.custom.SimpleStringSerializer)
}
}

View File

@ -9,7 +9,13 @@ import java.util.*
* A serializer that writes out a [BitSet] as an integer number of bits, plus the necessary number of bytes to encode that * A serializer that writes out a [BitSet] as an integer number of bits, plus the necessary number of bytes to encode that
* many bits. * many bits.
*/ */
class BitSetSerializer(factory: SerializerFactory) : CustomSerializer.Proxy<BitSet, BitSetSerializer.BitSetProxy>(BitSet::class.java, BitSetProxy::class.java, factory) { class BitSetSerializer(
factory: SerializerFactory
) : CustomSerializer.Proxy<BitSet, BitSetSerializer.BitSetProxy>(
BitSet::class.java,
BitSetProxy::class.java,
factory
) {
override fun toProxy(obj: BitSet): BitSetProxy = BitSetProxy(obj.toByteArray()) override fun toProxy(obj: BitSet): BitSetProxy = BitSetProxy(obj.toByteArray())
override fun fromProxy(proxy: BitSetProxy): BitSet = BitSet.valueOf(proxy.bytes) override fun fromProxy(proxy: BitSetProxy): BitSet = BitSet.valueOf(proxy.bytes)

View File

@ -8,8 +8,13 @@ import java.security.cert.CertPath
import java.security.cert.CertificateException import java.security.cert.CertificateException
import java.security.cert.CertificateFactory import java.security.cert.CertificateFactory
class CertPathSerializer(factory: SerializerFactory) class CertPathSerializer(
: CustomSerializer.Proxy<CertPath, CertPathSerializer.CertPathProxy>(CertPath::class.java, CertPathProxy::class.java, factory) { factory: SerializerFactory
) : CustomSerializer.Proxy<CertPath, CertPathSerializer.CertPathProxy>(
CertPath::class.java,
CertPathProxy::class.java,
factory
) {
override fun toProxy(obj: CertPath): CertPathProxy = CertPathProxy(obj.type, obj.encoded) override fun toProxy(obj: CertPath): CertPathProxy = CertPathProxy(obj.type, obj.encoded)
override fun fromProxy(proxy: CertPathProxy): CertPath { override fun fromProxy(proxy: CertPathProxy): CertPath {

View File

@ -6,7 +6,6 @@ import net.corda.core.utilities.trace
import net.corda.serialization.internal.amqp.AMQPNotSerializableException import net.corda.serialization.internal.amqp.AMQPNotSerializableException
import net.corda.serialization.internal.amqp.CustomSerializer import net.corda.serialization.internal.amqp.CustomSerializer
import net.corda.serialization.internal.amqp.LocalSerializerFactory import net.corda.serialization.internal.amqp.LocalSerializerFactory
import net.corda.serialization.internal.amqp.SerializerFactory
import net.corda.serialization.internal.amqp.custom.ClassSerializer.ClassProxy import net.corda.serialization.internal.amqp.custom.ClassSerializer.ClassProxy
/** /**

View File

@ -15,16 +15,27 @@ import java.security.PublicKey
* A serializer for [ContractAttachment] that uses a proxy object to write out the full attachment eagerly. * A serializer for [ContractAttachment] that uses a proxy object to write out the full attachment eagerly.
* @param factory the serializerFactory * @param factory the serializerFactory
*/ */
class ContractAttachmentSerializer(factory: SerializerFactory) : CustomSerializer.Proxy<ContractAttachment, class ContractAttachmentSerializer(
ContractAttachmentSerializer.ContractAttachmentProxy>(ContractAttachment::class.java, factory: SerializerFactory
ContractAttachmentProxy::class.java, factory) { ) : CustomSerializer.Proxy<ContractAttachment, ContractAttachmentSerializer.ContractAttachmentProxy>(
ContractAttachment::class.java,
ContractAttachmentProxy::class.java,
factory
) {
override fun toProxy(obj: ContractAttachment): ContractAttachmentProxy { override fun toProxy(obj: ContractAttachment): ContractAttachmentProxy {
val bytes = try { val bytes = try {
obj.attachment.open().readFully() obj.attachment.open().readFully()
} catch (e: Exception) { } catch (e: Exception) {
throw MissingAttachmentsException(listOf(obj.id)) throw MissingAttachmentsException(listOf(obj.id))
} }
return ContractAttachmentProxy(GeneratedAttachment(bytes, obj.uploader), obj.contract, obj.additionalContracts, obj.uploader, obj.signerKeys, obj.version) return ContractAttachmentProxy(
GeneratedAttachment(bytes, obj.uploader),
obj.contract,
obj.additionalContracts,
obj.uploader,
obj.signerKeys,
obj.version
)
} }
override fun fromProxy(proxy: ContractAttachmentProxy): ContractAttachment { override fun fromProxy(proxy: ContractAttachmentProxy): ContractAttachment {
@ -32,5 +43,12 @@ class ContractAttachmentSerializer(factory: SerializerFactory) : CustomSerialize
} }
@KeepForDJVM @KeepForDJVM
data class ContractAttachmentProxy(val attachment: Attachment, val contract: ContractClassName, val contracts: Set<ContractClassName>, val uploader: String?, val signers: List<PublicKey>, val version: Int) data class ContractAttachmentProxy(
val attachment: Attachment,
val contract: ContractClassName,
val contracts: Set<ContractClassName>,
val uploader: String?,
val signers: List<PublicKey>,
val version: Int
)
} }

View File

@ -6,7 +6,10 @@ import java.util.*
/** /**
* A custom serializer for the [Currency] class, utilizing the currency code string representation. * A custom serializer for the [Currency] class, utilizing the currency code string representation.
*/ */
object CurrencySerializer : CustomSerializer.ToString<Currency>(Currency::class.java, object CurrencySerializer
: CustomSerializer.ToString<Currency>(
Currency::class.java,
withInheritance = false, withInheritance = false,
maker = { Currency.getInstance(it) }, maker = { Currency.getInstance(it) },
unmaker = { it.currencyCode }) unmaker = { it.currencyCode }
)

View File

@ -8,7 +8,13 @@ import java.time.Duration
/** /**
* A serializer for [Duration] that uses a proxy object to write out the seconds and the nanos. * A serializer for [Duration] that uses a proxy object to write out the seconds and the nanos.
*/ */
class DurationSerializer(factory: SerializerFactory) : CustomSerializer.Proxy<Duration, DurationSerializer.DurationProxy>(Duration::class.java, DurationProxy::class.java, factory) { class DurationSerializer(
factory: SerializerFactory
) : CustomSerializer.Proxy<Duration, DurationSerializer.DurationProxy>(
Duration::class.java,
DurationProxy::class.java,
factory
) {
override fun toProxy(obj: Duration): DurationProxy = DurationProxy(obj.seconds, obj.nano) override fun toProxy(obj: Duration): DurationProxy = DurationProxy(obj.seconds, obj.nano)
override fun fromProxy(proxy: DurationProxy): Duration = Duration.ofSeconds(proxy.seconds, proxy.nanos.toLong()) override fun fromProxy(proxy: DurationProxy): Duration = Duration.ofSeconds(proxy.seconds, proxy.nanos.toLong())

View File

@ -10,7 +10,13 @@ import java.util.*
/** /**
* A serializer that writes out an [EnumSet] as a type, plus list of instances in the set. * A serializer that writes out an [EnumSet] as a type, plus list of instances in the set.
*/ */
class EnumSetSerializer(factory: SerializerFactory) : CustomSerializer.Proxy<EnumSet<*>, EnumSetSerializer.EnumSetProxy>(EnumSet::class.java, EnumSetProxy::class.java, factory) { class EnumSetSerializer(
factory: SerializerFactory
) : CustomSerializer.Proxy<EnumSet<*>, EnumSetSerializer.EnumSetProxy>(
EnumSet::class.java,
EnumSetProxy::class.java,
factory
) {
override val additionalSerializers: Iterable<CustomSerializer<out Any>> = listOf(ClassSerializer(factory)) override val additionalSerializers: Iterable<CustomSerializer<out Any>> = listOf(ClassSerializer(factory))
override fun toProxy(obj: EnumSet<*>): EnumSetProxy = EnumSetProxy(elementType(obj), obj.toList()) override fun toProxy(obj: EnumSet<*>): EnumSetProxy = EnumSetProxy(elementType(obj), obj.toList())

View File

@ -11,7 +11,10 @@ import java.lang.reflect.Type
/** /**
* A serializer that writes out the content of an input stream as bytes and deserializes into a [ByteArrayInputStream]. * A serializer that writes out the content of an input stream as bytes and deserializes into a [ByteArrayInputStream].
*/ */
object InputStreamSerializer : CustomSerializer.Implements<InputStream>(InputStream::class.java) { object InputStreamSerializer
: CustomSerializer.Implements<InputStream>(
InputStream::class.java
) {
override val revealSubclassesInSchema: Boolean = true override val revealSubclassesInSchema: Boolean = true
override val schemaForDocumentation = Schema( override val schemaForDocumentation = Schema(

View File

@ -8,7 +8,13 @@ import java.time.Instant
/** /**
* A serializer for [Instant] that uses a proxy object to write out the seconds since the epoch and the nanos. * A serializer for [Instant] that uses a proxy object to write out the seconds since the epoch and the nanos.
*/ */
class InstantSerializer(factory: SerializerFactory) : CustomSerializer.Proxy<Instant, InstantSerializer.InstantProxy>(Instant::class.java, InstantProxy::class.java, factory) { class InstantSerializer(
factory: SerializerFactory
) : CustomSerializer.Proxy<Instant, InstantSerializer.InstantProxy>(
Instant::class.java,
InstantProxy::class.java,
factory
) {
override fun toProxy(obj: Instant): InstantProxy = InstantProxy(obj.epochSecond, obj.nano) override fun toProxy(obj: Instant): InstantProxy = InstantProxy(obj.epochSecond, obj.nano)
override fun fromProxy(proxy: InstantProxy): Instant = Instant.ofEpochSecond(proxy.epochSeconds, proxy.nanos.toLong()) override fun fromProxy(proxy: InstantProxy): Instant = Instant.ofEpochSecond(proxy.epochSeconds, proxy.nanos.toLong())

View File

@ -8,7 +8,13 @@ import java.time.LocalDate
/** /**
* A serializer for [LocalDate] that uses a proxy object to write out the year, month and day. * A serializer for [LocalDate] that uses a proxy object to write out the year, month and day.
*/ */
class LocalDateSerializer(factory: SerializerFactory) : CustomSerializer.Proxy<LocalDate, LocalDateSerializer.LocalDateProxy>(LocalDate::class.java, LocalDateProxy::class.java, factory) { class LocalDateSerializer(
factory: SerializerFactory
) : CustomSerializer.Proxy<LocalDate, LocalDateSerializer.LocalDateProxy>(
LocalDate::class.java,
LocalDateProxy::class.java,
factory
) {
override fun toProxy(obj: LocalDate): LocalDateProxy = LocalDateProxy(obj.year, obj.monthValue.toByte(), obj.dayOfMonth.toByte()) override fun toProxy(obj: LocalDate): LocalDateProxy = LocalDateProxy(obj.year, obj.monthValue.toByte(), obj.dayOfMonth.toByte())
override fun fromProxy(proxy: LocalDateProxy): LocalDate = LocalDate.of(proxy.year, proxy.month.toInt(), proxy.day.toInt()) override fun fromProxy(proxy: LocalDateProxy): LocalDate = LocalDate.of(proxy.year, proxy.month.toInt(), proxy.day.toInt())

View File

@ -10,8 +10,17 @@ import java.time.LocalTime
/** /**
* A serializer for [LocalDateTime] that uses a proxy object to write out the date and time. * A serializer for [LocalDateTime] that uses a proxy object to write out the date and time.
*/ */
class LocalDateTimeSerializer(factory: SerializerFactory) : CustomSerializer.Proxy<LocalDateTime, LocalDateTimeSerializer.LocalDateTimeProxy>(LocalDateTime::class.java, LocalDateTimeProxy::class.java, factory) { class LocalDateTimeSerializer(
override val additionalSerializers: Iterable<CustomSerializer<out Any>> = listOf(LocalDateSerializer(factory), LocalTimeSerializer(factory)) factory: SerializerFactory
) : CustomSerializer.Proxy<LocalDateTime, LocalDateTimeSerializer.LocalDateTimeProxy>(
LocalDateTime::class.java,
LocalDateTimeProxy::class.java,
factory
) {
override val additionalSerializers: Iterable<CustomSerializer<out Any>> = listOf(
LocalDateSerializer(factory),
LocalTimeSerializer(factory)
)
override fun toProxy(obj: LocalDateTime): LocalDateTimeProxy = LocalDateTimeProxy(obj.toLocalDate(), obj.toLocalTime()) override fun toProxy(obj: LocalDateTime): LocalDateTimeProxy = LocalDateTimeProxy(obj.toLocalDate(), obj.toLocalTime())

View File

@ -8,10 +8,26 @@ import java.time.LocalTime
/** /**
* A serializer for [LocalTime] that uses a proxy object to write out the hours, minutes, seconds and the nanos. * A serializer for [LocalTime] that uses a proxy object to write out the hours, minutes, seconds and the nanos.
*/ */
class LocalTimeSerializer(factory: SerializerFactory) : CustomSerializer.Proxy<LocalTime, LocalTimeSerializer.LocalTimeProxy>(LocalTime::class.java, LocalTimeProxy::class.java, factory) { class LocalTimeSerializer(
override fun toProxy(obj: LocalTime): LocalTimeProxy = LocalTimeProxy(obj.hour.toByte(), obj.minute.toByte(), obj.second.toByte(), obj.nano) factory: SerializerFactory
) : CustomSerializer.Proxy<LocalTime, LocalTimeSerializer.LocalTimeProxy>(
LocalTime::class.java,
LocalTimeProxy::class.java,
factory
) {
override fun toProxy(obj: LocalTime): LocalTimeProxy = LocalTimeProxy(
obj.hour.toByte(),
obj.minute.toByte(),
obj.second.toByte(),
obj.nano
)
override fun fromProxy(proxy: LocalTimeProxy): LocalTime = LocalTime.of(proxy.hour.toInt(), proxy.minute.toInt(), proxy.second.toInt(), proxy.nano) override fun fromProxy(proxy: LocalTimeProxy): LocalTime = LocalTime.of(
proxy.hour.toInt(),
proxy.minute.toInt(),
proxy.second.toInt(),
proxy.nano
)
@KeepForDJVM @KeepForDJVM
data class LocalTimeProxy(val hour: Byte, val minute: Byte, val second: Byte, val nano: Int) data class LocalTimeProxy(val hour: Byte, val minute: Byte, val second: Byte, val nano: Int)

View File

@ -8,8 +8,9 @@ import java.time.MonthDay
/** /**
* A serializer for [MonthDay] that uses a proxy object to write out the integer form. * A serializer for [MonthDay] that uses a proxy object to write out the integer form.
*/ */
class MonthDaySerializer(factory: SerializerFactory) class MonthDaySerializer(
: CustomSerializer.Proxy<MonthDay, MonthDaySerializer.MonthDayProxy>( factory: SerializerFactory
) : CustomSerializer.Proxy<MonthDay, MonthDaySerializer.MonthDayProxy>(
MonthDay::class.java, MonthDayProxy::class.java, factory MonthDay::class.java, MonthDayProxy::class.java, factory
) { ) {
override fun toProxy(obj: MonthDay): MonthDayProxy = MonthDayProxy(obj.monthValue.toByte(), obj.dayOfMonth.toByte()) override fun toProxy(obj: MonthDay): MonthDayProxy = MonthDayProxy(obj.monthValue.toByte(), obj.dayOfMonth.toByte())

View File

@ -10,8 +10,17 @@ import java.time.ZoneOffset
/** /**
* A serializer for [OffsetDateTime] that uses a proxy object to write out the date and zone offset. * A serializer for [OffsetDateTime] that uses a proxy object to write out the date and zone offset.
*/ */
class OffsetDateTimeSerializer(factory: SerializerFactory) : CustomSerializer.Proxy<OffsetDateTime, OffsetDateTimeSerializer.OffsetDateTimeProxy>(OffsetDateTime::class.java, OffsetDateTimeProxy::class.java, factory) { class OffsetDateTimeSerializer(
override val additionalSerializers: Iterable<CustomSerializer<out Any>> = listOf(LocalDateTimeSerializer(factory), ZoneIdSerializer(factory)) factory: SerializerFactory
) : CustomSerializer.Proxy<OffsetDateTime, OffsetDateTimeSerializer.OffsetDateTimeProxy>(
OffsetDateTime::class.java,
OffsetDateTimeProxy::class.java,
factory
) {
override val additionalSerializers: Iterable<CustomSerializer<out Any>> = listOf(
LocalDateTimeSerializer(factory),
ZoneIdSerializer(factory)
)
override fun toProxy(obj: OffsetDateTime): OffsetDateTimeProxy = OffsetDateTimeProxy(obj.toLocalDateTime(), obj.offset) override fun toProxy(obj: OffsetDateTime): OffsetDateTimeProxy = OffsetDateTimeProxy(obj.toLocalDateTime(), obj.offset)

View File

@ -10,8 +10,17 @@ import java.time.ZoneOffset
/** /**
* A serializer for [OffsetTime] that uses a proxy object to write out the time and zone offset. * A serializer for [OffsetTime] that uses a proxy object to write out the time and zone offset.
*/ */
class OffsetTimeSerializer(factory: SerializerFactory) : CustomSerializer.Proxy<OffsetTime, OffsetTimeSerializer.OffsetTimeProxy>(OffsetTime::class.java, OffsetTimeProxy::class.java, factory) { class OffsetTimeSerializer(
override val additionalSerializers: Iterable<CustomSerializer<out Any>> = listOf(LocalTimeSerializer(factory), ZoneIdSerializer(factory)) factory: SerializerFactory
) : CustomSerializer.Proxy<OffsetTime, OffsetTimeSerializer.OffsetTimeProxy>(
OffsetTime::class.java,
OffsetTimeProxy::class.java,
factory
) {
override val additionalSerializers: Iterable<CustomSerializer<out Any>> = listOf(
LocalTimeSerializer(factory),
ZoneIdSerializer(factory)
)
override fun toProxy(obj: OffsetTime): OffsetTimeProxy = OffsetTimeProxy(obj.toLocalTime(), obj.offset) override fun toProxy(obj: OffsetTime): OffsetTimeProxy = OffsetTimeProxy(obj.toLocalTime(), obj.offset)

View File

@ -10,8 +10,13 @@ import net.corda.serialization.internal.amqp.SerializerFactory
* to save on network bandwidth * to save on network bandwidth
* Uses [OpaqueBytes] as a proxy * Uses [OpaqueBytes] as a proxy
*/ */
class OpaqueBytesSubSequenceSerializer(factory: SerializerFactory) : class OpaqueBytesSubSequenceSerializer(
CustomSerializer.Proxy<OpaqueBytesSubSequence, OpaqueBytes>(OpaqueBytesSubSequence::class.java, OpaqueBytes::class.java, factory) { factory: SerializerFactory
) : CustomSerializer.Proxy<OpaqueBytesSubSequence, OpaqueBytes>(
OpaqueBytesSubSequence::class.java,
OpaqueBytes::class.java,
factory
) {
override val additionalSerializers: Iterable<CustomSerializer<out Any>> = emptyList() override val additionalSerializers: Iterable<CustomSerializer<out Any>> = emptyList()
override fun toProxy(obj: OpaqueBytesSubSequence): OpaqueBytes = OpaqueBytes(obj.copyBytes()) override fun toProxy(obj: OpaqueBytesSubSequence): OpaqueBytes = OpaqueBytes(obj.copyBytes())
override fun fromProxy(proxy: OpaqueBytes): OpaqueBytesSubSequence = OpaqueBytesSubSequence(proxy.bytes, proxy.offset, proxy.size) override fun fromProxy(proxy: OpaqueBytes): OpaqueBytesSubSequence = OpaqueBytesSubSequence(proxy.bytes, proxy.offset, proxy.size)

View File

@ -3,13 +3,18 @@ package net.corda.serialization.internal.amqp.custom
import net.corda.core.KeepForDJVM import net.corda.core.KeepForDJVM
import net.corda.serialization.internal.amqp.CustomSerializer import net.corda.serialization.internal.amqp.CustomSerializer
import net.corda.serialization.internal.amqp.SerializerFactory import net.corda.serialization.internal.amqp.SerializerFactory
import java.time.OffsetTime
import java.util.* import java.util.*
/** /**
* A serializer for [Optional] that uses a proxy object to write out the value stored in the optional or [Optional.EMPTY]. * A serializer for [Optional] that uses a proxy object to write out the value stored in the optional or [Optional.EMPTY].
*/ */
class OptionalSerializer(factory: SerializerFactory) : CustomSerializer.Proxy<Optional<*>, OptionalSerializer.OptionalProxy>(Optional::class.java, OptionalProxy::class.java, factory) { class OptionalSerializer(
factory: SerializerFactory
) : CustomSerializer.Proxy<Optional<*>, OptionalSerializer.OptionalProxy>(
Optional::class.java,
OptionalProxy::class.java,
factory
) {
public override fun toProxy(obj: java.util.Optional<*>): OptionalProxy { public override fun toProxy(obj: java.util.Optional<*>): OptionalProxy {
return OptionalProxy(obj.orElse(null)) return OptionalProxy(obj.orElse(null))

View File

@ -8,7 +8,13 @@ import java.time.Period
/** /**
* A serializer for [Period] that uses a proxy object to write out the integer form. * A serializer for [Period] that uses a proxy object to write out the integer form.
*/ */
class PeriodSerializer(factory: SerializerFactory) : CustomSerializer.Proxy<Period, PeriodSerializer.PeriodProxy>(Period::class.java, PeriodProxy::class.java, factory) { class PeriodSerializer(
factory: SerializerFactory
) : CustomSerializer.Proxy<Period, PeriodSerializer.PeriodProxy>(
Period::class.java,
PeriodProxy::class.java,
factory
) {
override fun toProxy(obj: Period): PeriodProxy = PeriodProxy(obj.years, obj.months, obj.days) override fun toProxy(obj: Period): PeriodProxy = PeriodProxy(obj.years, obj.months, obj.days)
override fun fromProxy(proxy: PeriodProxy): Period = Period.of(proxy.years, proxy.months, proxy.days) override fun fromProxy(proxy: PeriodProxy): Period = Period.of(proxy.years, proxy.months, proxy.days)

View File

@ -9,9 +9,19 @@ import org.apache.qpid.proton.codec.Data
import java.lang.reflect.Type import java.lang.reflect.Type
import java.security.PrivateKey import java.security.PrivateKey
object PrivateKeySerializer : CustomSerializer.Implements<PrivateKey>(PrivateKey::class.java) { object PrivateKeySerializer
: CustomSerializer.Implements<PrivateKey>(
PrivateKey::class.java
) {
override val schemaForDocumentation = Schema(listOf(RestrictedType(type.toString(), "", listOf(type.toString()), AMQPTypeIdentifiers.primitiveTypeName(ByteArray::class.java), descriptor, emptyList()))) override val schemaForDocumentation = Schema(listOf(RestrictedType(
type.toString(),
"",
listOf(type.toString()),
AMQPTypeIdentifiers.primitiveTypeName(ByteArray::class.java),
descriptor,
emptyList()
)))
override fun writeDescribedObject(obj: PrivateKey, data: Data, type: Type, output: SerializationOutput, override fun writeDescribedObject(obj: PrivateKey, data: Data, type: Type, output: SerializationOutput,
context: SerializationContext context: SerializationContext

View File

@ -10,8 +10,18 @@ import java.security.PublicKey
/** /**
* A serializer that writes out a public key in X.509 format. * A serializer that writes out a public key in X.509 format.
*/ */
object PublicKeySerializer : CustomSerializer.Implements<PublicKey>(PublicKey::class.java) { object PublicKeySerializer
override val schemaForDocumentation = Schema(listOf(RestrictedType(type.toString(), "", listOf(type.toString()), AMQPTypeIdentifiers.primitiveTypeName(ByteArray::class.java), descriptor, emptyList()))) : CustomSerializer.Implements<PublicKey>(
PublicKey::class.java
) {
override val schemaForDocumentation = Schema(listOf(RestrictedType(
type.toString(),
"",
listOf(type.toString()),
AMQPTypeIdentifiers.primitiveTypeName(ByteArray::class.java),
descriptor,
emptyList()
)))
override fun writeDescribedObject(obj: PublicKey, data: Data, type: Type, output: SerializationOutput, override fun writeDescribedObject(obj: PublicKey, data: Data, type: Type, output: SerializationOutput,
context: SerializationContext context: SerializationContext

View File

@ -11,7 +11,13 @@ import net.corda.serialization.internal.model.LocalTypeInformation
import java.io.NotSerializableException import java.io.NotSerializableException
@KeepForDJVM @KeepForDJVM
class ThrowableSerializer(factory: LocalSerializerFactory) : CustomSerializer.Proxy<Throwable, ThrowableSerializer.ThrowableProxy>(Throwable::class.java, ThrowableProxy::class.java, factory) { class ThrowableSerializer(
factory: LocalSerializerFactory
) : CustomSerializer.Proxy<Throwable, ThrowableSerializer.ThrowableProxy>(
Throwable::class.java,
ThrowableProxy::class.java,
factory
) {
companion object { companion object {
private val logger = contextLogger() private val logger = contextLogger()

View File

@ -7,7 +7,10 @@ import java.lang.reflect.Type
import java.security.cert.CertificateFactory import java.security.cert.CertificateFactory
import java.security.cert.X509CRL import java.security.cert.X509CRL
object X509CRLSerializer : CustomSerializer.Implements<X509CRL>(X509CRL::class.java) { object X509CRLSerializer
: CustomSerializer.Implements<X509CRL>(
X509CRL::class.java
) {
override val schemaForDocumentation = Schema(listOf(RestrictedType( override val schemaForDocumentation = Schema(listOf(RestrictedType(
type.toString(), type.toString(),
"", "",

View File

@ -7,7 +7,10 @@ import java.lang.reflect.Type
import java.security.cert.CertificateFactory import java.security.cert.CertificateFactory
import java.security.cert.X509Certificate import java.security.cert.X509Certificate
object X509CertificateSerializer : CustomSerializer.Implements<X509Certificate>(X509Certificate::class.java) { object X509CertificateSerializer
: CustomSerializer.Implements<X509Certificate>(
X509Certificate::class.java
) {
override val schemaForDocumentation = Schema(listOf(RestrictedType( override val schemaForDocumentation = Schema(listOf(RestrictedType(
type.toString(), type.toString(),
"", "",

View File

@ -8,7 +8,13 @@ import java.time.YearMonth
/** /**
* A serializer for [YearMonth] that uses a proxy object to write out the integer form. * A serializer for [YearMonth] that uses a proxy object to write out the integer form.
*/ */
class YearMonthSerializer(factory: SerializerFactory) : CustomSerializer.Proxy<YearMonth, YearMonthSerializer.YearMonthProxy>(YearMonth::class.java, YearMonthProxy::class.java, factory) { class YearMonthSerializer(
factory: SerializerFactory
) : CustomSerializer.Proxy<YearMonth, YearMonthSerializer.YearMonthProxy>(
YearMonth::class.java,
YearMonthProxy::class.java,
factory
) {
override fun toProxy(obj: YearMonth): YearMonthProxy = YearMonthProxy(obj.year, obj.monthValue.toByte()) override fun toProxy(obj: YearMonth): YearMonthProxy = YearMonthProxy(obj.year, obj.monthValue.toByte())
override fun fromProxy(proxy: YearMonthProxy): YearMonth = YearMonth.of(proxy.year, proxy.month.toInt()) override fun fromProxy(proxy: YearMonthProxy): YearMonth = YearMonth.of(proxy.year, proxy.month.toInt())

View File

@ -8,7 +8,13 @@ import java.time.Year
/** /**
* A serializer for [Year] that uses a proxy object to write out the integer form. * A serializer for [Year] that uses a proxy object to write out the integer form.
*/ */
class YearSerializer(factory: SerializerFactory) : CustomSerializer.Proxy<Year, YearSerializer.YearProxy>(Year::class.java, YearProxy::class.java, factory) { class YearSerializer(
factory: SerializerFactory
) : CustomSerializer.Proxy<Year, YearSerializer.YearProxy>(
Year::class.java,
YearProxy::class.java,
factory
) {
override fun toProxy(obj: Year): YearProxy = YearProxy(obj.value) override fun toProxy(obj: Year): YearProxy = YearProxy(obj.value)
override fun fromProxy(proxy: YearProxy): Year = Year.of(proxy.year) override fun fromProxy(proxy: YearProxy): Year = Year.of(proxy.year)

View File

@ -8,7 +8,13 @@ import java.time.ZoneId
/** /**
* A serializer for [ZoneId] that uses a proxy object to write out the string form. * A serializer for [ZoneId] that uses a proxy object to write out the string form.
*/ */
class ZoneIdSerializer(factory: SerializerFactory) : CustomSerializer.Proxy<ZoneId, ZoneIdSerializer.ZoneIdProxy>(ZoneId::class.java, ZoneIdProxy::class.java, factory) { class ZoneIdSerializer(
factory: SerializerFactory
) : CustomSerializer.Proxy<ZoneId, ZoneIdSerializer.ZoneIdProxy>(
ZoneId::class.java,
ZoneIdProxy::class.java,
factory
) {
override val revealSubclassesInSchema: Boolean = true override val revealSubclassesInSchema: Boolean = true
override fun toProxy(obj: ZoneId): ZoneIdProxy = ZoneIdProxy(obj.id) override fun toProxy(obj: ZoneId): ZoneIdProxy = ZoneIdProxy(obj.id)

View File

@ -12,22 +12,41 @@ import java.time.ZonedDateTime
/** /**
* A serializer for [ZonedDateTime] that uses a proxy object to write out the date, time, offset and zone. * A serializer for [ZonedDateTime] that uses a proxy object to write out the date, time, offset and zone.
*/ */
class ZonedDateTimeSerializer(factory: SerializerFactory) : CustomSerializer.Proxy<ZonedDateTime, ZonedDateTimeSerializer.ZonedDateTimeProxy>(ZonedDateTime::class.java, ZonedDateTimeProxy::class.java, factory) { class ZonedDateTimeSerializer(
factory: SerializerFactory
) : CustomSerializer.Proxy<ZonedDateTime, ZonedDateTimeSerializer.ZonedDateTimeProxy>(
ZonedDateTime::class.java,
ZonedDateTimeProxy::class.java,
factory
) {
// Java deserialization of `ZonedDateTime` uses a private method. We will resolve this somewhat statically // Java deserialization of `ZonedDateTime` uses a private method. We will resolve this somewhat statically
// so that any change to internals of `ZonedDateTime` is detected early. // so that any change to internals of `ZonedDateTime` is detected early.
companion object { companion object {
val ofLenient: Method = ZonedDateTime::class.java.getDeclaredMethod("ofLenient", LocalDateTime::class.java, ZoneOffset::class.java, ZoneId::class.java) val ofLenient: Method = ZonedDateTime::class.java.getDeclaredMethod(
"ofLenient",
LocalDateTime::class.java,
ZoneOffset::class.java,
ZoneId::class.java
)
init { init {
ofLenient.isAccessible = true ofLenient.isAccessible = true
} }
} }
override val additionalSerializers: Iterable<CustomSerializer<out Any>> = listOf(LocalDateTimeSerializer(factory), ZoneIdSerializer(factory)) override val additionalSerializers: Iterable<CustomSerializer<out Any>> = listOf(
LocalDateTimeSerializer(factory),
ZoneIdSerializer(factory)
)
override fun toProxy(obj: ZonedDateTime): ZonedDateTimeProxy = ZonedDateTimeProxy(obj.toLocalDateTime(), obj.offset, obj.zone) override fun toProxy(obj: ZonedDateTime): ZonedDateTimeProxy = ZonedDateTimeProxy(obj.toLocalDateTime(), obj.offset, obj.zone)
override fun fromProxy(proxy: ZonedDateTimeProxy): ZonedDateTime = ofLenient.invoke(null, proxy.dateTime, proxy.offset, proxy.zone) as ZonedDateTime override fun fromProxy(proxy: ZonedDateTimeProxy): ZonedDateTime = ofLenient.invoke(
null,
proxy.dateTime,
proxy.offset,
proxy.zone
) as ZonedDateTime
@KeepForDJVM @KeepForDJVM
data class ZonedDateTimeProxy(val dateTime: LocalDateTime, val offset: ZoneOffset, val zone: ZoneId) data class ZonedDateTimeProxy(val dateTime: LocalDateTime, val offset: ZoneOffset, val zone: ZoneId)

View File

@ -3,8 +3,17 @@ ENV GRADLE_USER_HOME=/tmp/gradle
RUN mkdir /tmp/gradle && mkdir -p /home/root/.m2/repository RUN mkdir /tmp/gradle && mkdir -p /home/root/.m2/repository
RUN apt-get update && apt-get install -y curl libatomic1 && \ RUN apt-get update && apt-get install -y curl libatomic1 && \
curl -O https://d3pxv6yz143wms.cloudfront.net/8.222.10.1/java-1.8.0-amazon-corretto-jdk_8.222.10-1_amd64.deb && \ curl -O https://cdn.azul.com/zulu/bin/zulu8.40.0.25-ca-jdk8.0.222-linux_amd64.deb && \
apt-get install -y java-common && dpkg -i java-1.8.0-amazon-corretto-jdk_8.222.10-1_amd64.deb && \ apt-get install -y java-common && apt install -y ./zulu8.40.0.25-ca-jdk8.0.222-linux_amd64.deb && \
apt-get clean && \ apt-get clean && \
mkdir -p /tmp/source rm -f zulu8.40.0.25-ca-jdk8.0.222-linux_amd64.deb && \
curl -O https://cdn.azul.com/zulu/bin/zulu8.40.0.25-ca-fx-jdk8.0.222-linux_x64.tar.gz && \
mv /zulu8.40.0.25-ca-fx-jdk8.0.222-linux_x64.tar.gz /usr/lib/jvm/ && \
cd /usr/lib/jvm/ && \
tar -zxvf zulu8.40.0.25-ca-fx-jdk8.0.222-linux_x64.tar.gz && \
rm -rf zulu-8-amd64 && \
mv zulu8.40.0.25-ca-fx-jdk8.0.222-linux_x64 zulu-8-amd64 && \
rm -f zulu8.40.0.25-ca-fx-jdk8.0.222-linux_x64.tar.gz && \
cd / && mkdir -p /tmp/source

View File

@ -199,7 +199,8 @@ fun <A> driver(defaultParameters: DriverParameters = DriverParameters(), dsl: Dr
networkParameters = defaultParameters.networkParameters, networkParameters = defaultParameters.networkParameters,
notaryCustomOverrides = defaultParameters.notaryCustomOverrides, notaryCustomOverrides = defaultParameters.notaryCustomOverrides,
inMemoryDB = defaultParameters.inMemoryDB, inMemoryDB = defaultParameters.inMemoryDB,
cordappsForAllNodes = uncheckedCast(defaultParameters.cordappsForAllNodes) cordappsForAllNodes = uncheckedCast(defaultParameters.cordappsForAllNodes),
environmentVariables = defaultParameters.environmentVariables
), ),
coerce = { it }, coerce = { it },
dsl = dsl dsl = dsl
@ -255,10 +256,46 @@ data class DriverParameters(
val networkParameters: NetworkParameters = testNetworkParameters(notaries = emptyList()), val networkParameters: NetworkParameters = testNetworkParameters(notaries = emptyList()),
val notaryCustomOverrides: Map<String, Any?> = emptyMap(), val notaryCustomOverrides: Map<String, Any?> = emptyMap(),
val inMemoryDB: Boolean = true, val inMemoryDB: Boolean = true,
val cordappsForAllNodes: Collection<TestCordapp>? = null val cordappsForAllNodes: Collection<TestCordapp>? = null,
val environmentVariables : Map<String, String> = emptyMap()
) { ) {
constructor(cordappsForAllNodes: Collection<TestCordapp>) : this(isDebug = false, cordappsForAllNodes = cordappsForAllNodes) constructor(cordappsForAllNodes: Collection<TestCordapp>) : this(isDebug = false, cordappsForAllNodes = cordappsForAllNodes)
constructor(
isDebug: Boolean = false,
driverDirectory: Path = Paths.get("build") / "node-driver" / getTimestampAsDirectoryName(),
portAllocation: PortAllocation = incrementalPortAllocation(),
debugPortAllocation: PortAllocation = incrementalPortAllocation(),
systemProperties: Map<String, String> = emptyMap(),
useTestClock: Boolean = false,
startNodesInProcess: Boolean = false,
waitForAllNodesToFinish: Boolean = false,
notarySpecs: List<NotarySpec> = listOf(NotarySpec(DUMMY_NOTARY_NAME)),
extraCordappPackagesToScan: List<String> = emptyList(),
@Suppress("DEPRECATION") jmxPolicy: JmxPolicy = JmxPolicy(),
networkParameters: NetworkParameters = testNetworkParameters(notaries = emptyList()),
notaryCustomOverrides: Map<String, Any?> = emptyMap(),
inMemoryDB: Boolean = true,
cordappsForAllNodes: Collection<TestCordapp>? = null
) : this(
isDebug,
driverDirectory,
portAllocation,
debugPortAllocation,
systemProperties,
useTestClock,
startNodesInProcess,
waitForAllNodesToFinish,
notarySpecs,
extraCordappPackagesToScan,
jmxPolicy,
networkParameters,
notaryCustomOverrides,
inMemoryDB,
cordappsForAllNodes,
environmentVariables = emptyMap()
)
constructor( constructor(
isDebug: Boolean = false, isDebug: Boolean = false,
driverDirectory: Path = Paths.get("build") / "node-driver" / getTimestampAsDirectoryName(), driverDirectory: Path = Paths.get("build") / "node-driver" / getTimestampAsDirectoryName(),
@ -373,6 +410,7 @@ data class DriverParameters(
fun withNotaryCustomOverrides(notaryCustomOverrides: Map<String, Any?>): DriverParameters = copy(notaryCustomOverrides = notaryCustomOverrides) fun withNotaryCustomOverrides(notaryCustomOverrides: Map<String, Any?>): DriverParameters = copy(notaryCustomOverrides = notaryCustomOverrides)
fun withInMemoryDB(inMemoryDB: Boolean): DriverParameters = copy(inMemoryDB = inMemoryDB) fun withInMemoryDB(inMemoryDB: Boolean): DriverParameters = copy(inMemoryDB = inMemoryDB)
fun withCordappsForAllNodes(cordappsForAllNodes: Collection<TestCordapp>?): DriverParameters = copy(cordappsForAllNodes = cordappsForAllNodes) fun withCordappsForAllNodes(cordappsForAllNodes: Collection<TestCordapp>?): DriverParameters = copy(cordappsForAllNodes = cordappsForAllNodes)
fun withEnvironmentVariables(variables : Map<String, String>): DriverParameters = copy(environmentVariables = variables)
fun copy( fun copy(
isDebug: Boolean, isDebug: Boolean,
@ -433,4 +471,40 @@ data class DriverParameters(
notaryCustomOverrides = emptyMap(), notaryCustomOverrides = emptyMap(),
cordappsForAllNodes = cordappsForAllNodes cordappsForAllNodes = cordappsForAllNodes
) )
@Suppress("LongParameterList")
fun copy(
isDebug: Boolean,
driverDirectory: Path,
portAllocation: PortAllocation,
debugPortAllocation: PortAllocation,
systemProperties: Map<String, String>,
useTestClock: Boolean,
startNodesInProcess: Boolean,
waitForAllNodesToFinish: Boolean,
notarySpecs: List<NotarySpec>,
extraCordappPackagesToScan: List<String>,
jmxPolicy: JmxPolicy,
networkParameters: NetworkParameters,
notaryCustomOverrides: Map<String, Any?>,
inMemoryDB: Boolean,
cordappsForAllNodes: Collection<TestCordapp>?
) = this.copy(
isDebug = isDebug,
driverDirectory = driverDirectory,
portAllocation = portAllocation,
debugPortAllocation = debugPortAllocation,
systemProperties = systemProperties,
useTestClock = useTestClock,
startNodesInProcess = startNodesInProcess,
waitForAllNodesToFinish = waitForAllNodesToFinish,
notarySpecs = notarySpecs,
extraCordappPackagesToScan = extraCordappPackagesToScan,
jmxPolicy = jmxPolicy,
networkParameters = networkParameters,
notaryCustomOverrides = notaryCustomOverrides,
inMemoryDB = inMemoryDB,
cordappsForAllNodes = cordappsForAllNodes,
environmentVariables = emptyMap()
)
} }

View File

@ -91,7 +91,8 @@ class DriverDSLImpl(
val networkParameters: NetworkParameters, val networkParameters: NetworkParameters,
val notaryCustomOverrides: Map<String, Any?>, val notaryCustomOverrides: Map<String, Any?>,
val inMemoryDB: Boolean, val inMemoryDB: Boolean,
val cordappsForAllNodes: Collection<TestCordappInternal>? val cordappsForAllNodes: Collection<TestCordappInternal>?,
val environmentVariables : Map<String, String>
) : InternalDriverDSL { ) : InternalDriverDSL {
private var _executorService: ScheduledExecutorService? = null private var _executorService: ScheduledExecutorService? = null
@ -288,9 +289,11 @@ class DriverDSLImpl(
} else { } else {
startOutOfProcessMiniNode( startOutOfProcessMiniNode(
config, config,
"initial-registration", arrayOf(
"--network-root-truststore=${rootTruststorePath.toAbsolutePath()}", "initial-registration",
"--network-root-truststore-password=$rootTruststorePassword" "--network-root-truststore=${rootTruststorePath.toAbsolutePath()}",
"--network-root-truststore-password=$rootTruststorePassword"
)
).map { config } ).map { config }
} }
} }
@ -450,7 +453,7 @@ class DriverDSLImpl(
} else { } else {
// TODO The config we use here is uses a hardocded p2p port which changes when the node is run proper // TODO The config we use here is uses a hardocded p2p port which changes when the node is run proper
// This causes two node info files to be generated. // This causes two node info files to be generated.
startOutOfProcessMiniNode(config, "generate-node-info").map { startOutOfProcessMiniNode(config, arrayOf("generate-node-info")).map {
// Once done we have to read the signed node info file that's been generated // Once done we have to read the signed node info file that's been generated
val nodeInfoFile = config.corda.baseDirectory.list { paths -> val nodeInfoFile = config.corda.baseDirectory.list { paths ->
paths.filter { it.fileName.toString().startsWith(NodeInfoFilesCopier.NODE_INFO_FILE_NAME_PREFIX) }.findFirst().get() paths.filter { it.fileName.toString().startsWith(NodeInfoFilesCopier.NODE_INFO_FILE_NAME_PREFIX) }.findFirst().get()
@ -536,7 +539,7 @@ class DriverDSLImpl(
* Start the node with the given flag which is expected to start the node for some function, which once complete will * Start the node with the given flag which is expected to start the node for some function, which once complete will
* terminate the node. * terminate the node.
*/ */
private fun startOutOfProcessMiniNode(config: NodeConfig, vararg extraCmdLineFlag: String): CordaFuture<Unit> { private fun startOutOfProcessMiniNode(config: NodeConfig, extraCmdLineFlag: Array<String> = emptyArray()): CordaFuture<Unit> {
val debugPort = if (isDebug) debugPortAllocation.nextPort() else null val debugPort = if (isDebug) debugPortAllocation.nextPort() else null
val process = startOutOfProcessNode( val process = startOutOfProcessNode(
config, config,
@ -545,7 +548,8 @@ class DriverDSLImpl(
systemProperties, systemProperties,
"512m", "512m",
null, null,
*extraCmdLineFlag environmentVariables,
extraCmdLineFlag
) )
return poll(executorService, "$extraCmdLineFlag (${config.corda.myLegalName})") { return poll(executorService, "$extraCmdLineFlag (${config.corda.myLegalName})") {
@ -602,7 +606,15 @@ class DriverDSLImpl(
nodeFuture nodeFuture
} else { } else {
val debugPort = if (isDebug) debugPortAllocation.nextPort() else null val debugPort = if (isDebug) debugPortAllocation.nextPort() else null
val process = startOutOfProcessNode(config, quasarJarPath, debugPort, systemProperties, parameters.maximumHeapSize, parameters.logLevelOverride) val process = startOutOfProcessNode(
config,
quasarJarPath,
debugPort,
systemProperties,
parameters.maximumHeapSize,
parameters.logLevelOverride,
environmentVariables
)
// Destroy the child process when the parent exits.This is needed even when `waitForAllNodesToFinish` is // Destroy the child process when the parent exits.This is needed even when `waitForAllNodesToFinish` is
// true because we don't want orphaned processes in the case that the parent process is terminated by the // true because we don't want orphaned processes in the case that the parent process is terminated by the
@ -726,6 +738,7 @@ class DriverDSLImpl(
} }
} }
@Suppress("LongParameterList")
private fun startOutOfProcessNode( private fun startOutOfProcessNode(
config: NodeConfig, config: NodeConfig,
quasarJarPath: String, quasarJarPath: String,
@ -733,9 +746,11 @@ class DriverDSLImpl(
overriddenSystemProperties: Map<String, String>, overriddenSystemProperties: Map<String, String>,
maximumHeapSize: String, maximumHeapSize: String,
logLevelOverride: String?, logLevelOverride: String?,
vararg extraCmdLineFlag: String environmentVariables : Map<String,String>,
): Process { extraCmdLineFlag: Array<String> = emptyArray()
log.info("Starting out-of-process Node ${config.corda.myLegalName.organisation}, debug port is " + (debugPort ?: "not enabled")) ): Process {
log.info("Starting out-of-process Node ${config.corda.myLegalName.organisation}, " +
"debug port is " + (debugPort ?: "not enabled"))
// Write node.conf // Write node.conf
writeConfig(config.corda.baseDirectory, "node.conf", config.typesafe.toNodeOnly()) writeConfig(config.corda.baseDirectory, "node.conf", config.typesafe.toNodeOnly())
@ -774,7 +789,7 @@ class DriverDSLImpl(
"--base-directory=${config.corda.baseDirectory}", "--base-directory=${config.corda.baseDirectory}",
"--logging-level=$loggingLevel", "--logging-level=$loggingLevel",
"--no-local-shell").also { "--no-local-shell").also {
it += extraCmdLineFlag it.addAll(extraCmdLineFlag)
}.toList() }.toList()
// The following dependencies are excluded from the classpath of the created JVM, so that the environment resembles a real one as close as possible. // The following dependencies are excluded from the classpath of the created JVM, so that the environment resembles a real one as close as possible.
@ -792,7 +807,8 @@ class DriverDSLImpl(
extraJvmArguments = extraJvmArguments, extraJvmArguments = extraJvmArguments,
workingDirectory = config.corda.baseDirectory, workingDirectory = config.corda.baseDirectory,
maximumHeapSize = maximumHeapSize, maximumHeapSize = maximumHeapSize,
classPath = cp classPath = cp,
environmentVariables = environmentVariables
) )
} }
@ -1013,7 +1029,8 @@ fun <DI : DriverDSL, D : InternalDriverDSL, A> genericDriver(
networkParameters = defaultParameters.networkParameters, networkParameters = defaultParameters.networkParameters,
notaryCustomOverrides = defaultParameters.notaryCustomOverrides, notaryCustomOverrides = defaultParameters.notaryCustomOverrides,
inMemoryDB = defaultParameters.inMemoryDB, inMemoryDB = defaultParameters.inMemoryDB,
cordappsForAllNodes = uncheckedCast(defaultParameters.cordappsForAllNodes) cordappsForAllNodes = uncheckedCast(defaultParameters.cordappsForAllNodes),
environmentVariables = defaultParameters.environmentVariables
) )
) )
val shutdownHook = addShutdownHook(driverDsl::shutdown) val shutdownHook = addShutdownHook(driverDsl::shutdown)
@ -1090,6 +1107,7 @@ class SplitCompatibilityZoneParams(
override fun config() : NetworkServicesConfig = config override fun config() : NetworkServicesConfig = config
} }
@Suppress("LongParameterList")
fun <A> internalDriver( fun <A> internalDriver(
isDebug: Boolean = DriverParameters().isDebug, isDebug: Boolean = DriverParameters().isDebug,
driverDirectory: Path = DriverParameters().driverDirectory, driverDirectory: Path = DriverParameters().driverDirectory,
@ -1107,6 +1125,7 @@ fun <A> internalDriver(
notaryCustomOverrides: Map<String, Any?> = DriverParameters().notaryCustomOverrides, notaryCustomOverrides: Map<String, Any?> = DriverParameters().notaryCustomOverrides,
inMemoryDB: Boolean = DriverParameters().inMemoryDB, inMemoryDB: Boolean = DriverParameters().inMemoryDB,
cordappsForAllNodes: Collection<TestCordappInternal>? = null, cordappsForAllNodes: Collection<TestCordappInternal>? = null,
environmentVariables: Map<String, String> = emptyMap(),
dsl: DriverDSLImpl.() -> A dsl: DriverDSLImpl.() -> A
): A { ): A {
return genericDriver( return genericDriver(
@ -1126,7 +1145,8 @@ fun <A> internalDriver(
networkParameters = networkParameters, networkParameters = networkParameters,
notaryCustomOverrides = notaryCustomOverrides, notaryCustomOverrides = notaryCustomOverrides,
inMemoryDB = inMemoryDB, inMemoryDB = inMemoryDB,
cordappsForAllNodes = cordappsForAllNodes cordappsForAllNodes = cordappsForAllNodes,
environmentVariables = environmentVariables
), ),
coerce = { it }, coerce = { it },
dsl = dsl dsl = dsl

View File

@ -7,17 +7,29 @@ import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
object ProcessUtilities { object ProcessUtilities {
@Suppress("LongParameterList")
inline fun <reified C : Any> startJavaProcess( inline fun <reified C : Any> startJavaProcess(
arguments: List<String>, arguments: List<String>,
classPath: List<String> = defaultClassPath, classPath: List<String> = defaultClassPath,
workingDirectory: Path? = null, workingDirectory: Path? = null,
jdwpPort: Int? = null, jdwpPort: Int? = null,
extraJvmArguments: List<String> = emptyList(), extraJvmArguments: List<String> = emptyList(),
maximumHeapSize: String? = null maximumHeapSize: String? = null,
environmentVariables: Map<String, String> = emptyMap()
): Process { ): Process {
return startJavaProcess(C::class.java.name, arguments, classPath, workingDirectory, jdwpPort, extraJvmArguments, maximumHeapSize) return startJavaProcess(
C::class.java.name,
arguments,
classPath,
workingDirectory,
jdwpPort,
extraJvmArguments,
maximumHeapSize,
environmentVariables
)
} }
@Suppress("LongParameterList")
fun startJavaProcess( fun startJavaProcess(
className: String, className: String,
arguments: List<String>, arguments: List<String>,
@ -25,7 +37,8 @@ object ProcessUtilities {
workingDirectory: Path? = null, workingDirectory: Path? = null,
jdwpPort: Int? = null, jdwpPort: Int? = null,
extraJvmArguments: List<String> = emptyList(), extraJvmArguments: List<String> = emptyList(),
maximumHeapSize: String? = null maximumHeapSize: String? = null,
environmentVariables: Map<String,String> = emptyMap()
): Process { ): Process {
val command = mutableListOf<String>().apply { val command = mutableListOf<String>().apply {
add(javaPath) add(javaPath)
@ -38,6 +51,7 @@ object ProcessUtilities {
} }
return ProcessBuilder(command).apply { return ProcessBuilder(command).apply {
inheritIO() inheritIO()
environment().putAll(environmentVariables)
environment()["CLASSPATH"] = classPath.joinToString(File.pathSeparator) environment()["CLASSPATH"] = classPath.joinToString(File.pathSeparator)
if (workingDirectory != null) { if (workingDirectory != null) {
// Timestamp may be handy if the same process started, killed and then re-started. Without timestamp // Timestamp may be handy if the same process started, killed and then re-started. Without timestamp

View File

@ -104,6 +104,7 @@ val fakeNodeLegalName = CordaX500Name(organisation = "Not:a:real:name", locality
private val globalPortAllocation = incrementalPortAllocation() private val globalPortAllocation = incrementalPortAllocation()
private val globalDebugPortAllocation = incrementalPortAllocation() private val globalDebugPortAllocation = incrementalPortAllocation()
@Suppress("LongParameterList")
fun <A> rpcDriver( fun <A> rpcDriver(
isDebug: Boolean = false, isDebug: Boolean = false,
driverDirectory: Path = Paths.get("build") / "rpc-driver" / getTimestampAsDirectoryName(), driverDirectory: Path = Paths.get("build") / "rpc-driver" / getTimestampAsDirectoryName(),
@ -121,6 +122,7 @@ fun <A> rpcDriver(
notaryCustomOverrides: Map<String, Any?> = emptyMap(), notaryCustomOverrides: Map<String, Any?> = emptyMap(),
inMemoryDB: Boolean = true, inMemoryDB: Boolean = true,
cordappsForAllNodes: Collection<TestCordappInternal>? = null, cordappsForAllNodes: Collection<TestCordappInternal>? = null,
environmentVariables: Map<String, String> = emptyMap(),
dsl: RPCDriverDSL.() -> A dsl: RPCDriverDSL.() -> A
): A { ): A {
return genericDriver( return genericDriver(
@ -141,7 +143,8 @@ fun <A> rpcDriver(
networkParameters = networkParameters, networkParameters = networkParameters,
notaryCustomOverrides = notaryCustomOverrides, notaryCustomOverrides = notaryCustomOverrides,
inMemoryDB = inMemoryDB, inMemoryDB = inMemoryDB,
cordappsForAllNodes = cordappsForAllNodes cordappsForAllNodes = cordappsForAllNodes,
environmentVariables = environmentVariables
), externalTrace ), externalTrace
), ),
coerce = { it }, coerce = { it },

View File

@ -138,7 +138,7 @@ object InteractiveShell {
ExternalResolver.INSTANCE.addCommand("hashLookup", "Checks if a transaction with matching Id hash exists.", HashLookupShellCommand::class.java) ExternalResolver.INSTANCE.addCommand("hashLookup", "Checks if a transaction with matching Id hash exists.", HashLookupShellCommand::class.java)
ExternalResolver.INSTANCE.addCommand("attachments", "Commands to extract information about attachments stored within the node", AttachmentShellCommand::class.java) ExternalResolver.INSTANCE.addCommand("attachments", "Commands to extract information about attachments stored within the node", AttachmentShellCommand::class.java)
ExternalResolver.INSTANCE.addCommand("checkpoints", "Commands to extract information about checkpoints stored within the node", CheckpointShellCommand::class.java) ExternalResolver.INSTANCE.addCommand("checkpoints", "Commands to extract information about checkpoints stored within the node", CheckpointShellCommand::class.java)
shell = ShellLifecycle(configuration.commandsDirectory).start(config, configuration.user, configuration.password) shell = ShellLifecycle(configuration.commandsDirectory).start(config, configuration.user, configuration.password, runSshDaemon)
} }
fun runLocalShell(onExit: () -> Unit = {}) { fun runLocalShell(onExit: () -> Unit = {}) {
@ -166,7 +166,7 @@ object InteractiveShell {
} }
class ShellLifecycle(private val shellCommands: Path) : PluginLifeCycle() { class ShellLifecycle(private val shellCommands: Path) : PluginLifeCycle() {
fun start(config: Properties, localUserName: String = "", localUserPassword: String = ""): Shell { fun start(config: Properties, localUserName: String = "", localUserPassword: String = "", runSshDaemon: Boolean): Shell {
val classLoader = this.javaClass.classLoader val classLoader = this.javaClass.classLoader
val classpathDriver = ClassPathMountFactory(classLoader) val classpathDriver = ClassPathMountFactory(classLoader)
val fileDriver = FileMountFactory(Utils.getCurrentDirectory()) val fileDriver = FileMountFactory(Utils.getCurrentDirectory())
@ -192,7 +192,8 @@ object InteractiveShell {
return super.getPlugins().filterNot { it is JavaLanguage } + CordaAuthenticationPlugin(rpcOps) return super.getPlugins().filterNot { it is JavaLanguage } + CordaAuthenticationPlugin(rpcOps)
} }
} }
val attributes = emptyMap<String, Any>() val attributes = mutableMapOf<String, Any>()
attributes["crash.localShell"] = runSshDaemon
val context = PluginContext(discovery, attributes, commandsFS, confFS, classLoader) val context = PluginContext(discovery, attributes, commandsFS, confFS, classLoader)
context.refresh() context.refresh()
this.config = config this.config = config

View File

@ -175,11 +175,13 @@ abstract class ANSIProgressRenderer {
var indent = 0 var indent = 0
while (errorToPrint != null) { while (errorToPrint != null) {
ansi.fgRed() ansi.fgRed()
ansi.a("${IntStream.range(indent, indent).mapToObj { "\t" }.toList().joinToString(separator = "") { s -> s }} $errorIcon ${error.message}") ansi.a("${"\t".repeat(indent)}$errorIcon ${errorToPrint.message}")
ansi.reset() ansi.newline()
errorToPrint = errorToPrint.cause errorToPrint = errorToPrint.cause
indent++ indent++
} }
ansi.reset()
ansi.eraseLine(Ansi.Erase.FORWARD) ansi.eraseLine(Ansi.Erase.FORWARD)
ansi.newline() ansi.newline()
newLinesDrawn++ newLinesDrawn++

View File

@ -3,12 +3,23 @@ package net.corda.tools.shell.base
// Note that this file MUST be in a sub-directory called "base" relative to the path // Note that this file MUST be in a sub-directory called "base" relative to the path
// given in the configuration code in InteractiveShell. // given in the configuration code in InteractiveShell.
welcome = """ welcome = { ->
if (crash.context.attributes["crash.localShell"] == true) {
"""
Welcome to the Corda interactive shell. Welcome to the Corda interactive shell.
Useful commands include 'help' to see what is available, and 'bye' to shut down the node. Useful commands include 'help' to see what is available, and 'bye' to exit the shell.
""" """.stripIndent()
} else {
"""
Welcome to the Corda interactive shell.
Useful commands include 'help' to see what is available, and 'bye' to shut down the node.
""".stripIndent()
}
}
prompt = { -> prompt = { ->
return "${new Date()}>>> " return "${new Date()}>>> "