mirror of
https://github.com/corda/corda.git
synced 2024-12-18 20:47:57 +00:00
Merge branch 'release/os/4.3' into release/os/4.4
# Conflicts: # docs/source/docker-image.rst
This commit is contained in:
commit
0a194cfb51
26
.ci/dev/unit/Jenkinsfile
vendored
26
.ci/dev/unit/Jenkinsfile
vendored
@ -29,20 +29,18 @@ pipeline {
|
||||
}
|
||||
}
|
||||
|
||||
stage('Corda Pull Request - Run Tests') {
|
||||
stage('Unit Tests') {
|
||||
steps {
|
||||
sh "./gradlew " +
|
||||
"-DbuildId=\"\${BUILD_ID}\" " +
|
||||
"-Dkubenetize=true " +
|
||||
"-Ddocker.tag=\"\${DOCKER_TAG_TO_USE}\"" +
|
||||
" allParallelUnitTest"
|
||||
if (env.CHANGE_ID) {
|
||||
pullRequest.createStatus(status: 'success',
|
||||
context: 'continuous-integration/jenkins/pr-merge/unitTest',
|
||||
description: 'Unit Tests Passed',
|
||||
targetUrl: "${env.JOB_URL}/testResults")
|
||||
}
|
||||
stage('Unit Tests') {
|
||||
steps {
|
||||
sh "./gradlew " +
|
||||
"-DbuildId=\"\${BUILD_ID}\" " +
|
||||
"-Dkubenetize=true " +
|
||||
"-Ddocker.tag=\"\${DOCKER_TAG_TO_USE}\"" +
|
||||
" allParallelUnitTest"
|
||||
if (env.CHANGE_ID) {
|
||||
pullRequest.createStatus(status: 'success',
|
||||
context: 'continuous-integration/jenkins/pr-merge/unitTest',
|
||||
description: 'Unit Tests Passed',
|
||||
targetUrl: "${env.JOB_URL}/testResults")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
10
.idea/codeStyles/Project.xml
generated
10
.idea/codeStyles/Project.xml
generated
@ -3,6 +3,14 @@
|
||||
<option name="LINE_SEPARATOR" value=" " />
|
||||
<option name="RIGHT_MARGIN" 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>
|
||||
<option name="PACKAGES_TO_USE_STAR_IMPORTS">
|
||||
<value>
|
||||
@ -11,6 +19,8 @@
|
||||
<package name="tornadofx" withSubpackages="false" static="false" />
|
||||
</value>
|
||||
</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_ARGUMENT_LISTS" value="true" />
|
||||
<option name="CONTINUATION_INDENT_FOR_EXPRESSION_BODIES" value="true" />
|
||||
|
1
.idea/codeStyles/codeStyleConfig.xml
generated
1
.idea/codeStyles/codeStyleConfig.xml
generated
@ -1,6 +1,5 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
|
||||
</state>
|
||||
</component>
|
3
Jenkinsfile
vendored
3
Jenkinsfile
vendored
@ -1,3 +1,4 @@
|
||||
import static com.r3.build.BuildControl.killAllExistingBuildsForJob
|
||||
@Library('existing-build-control')
|
||||
import static com.r3.build.BuildControl.killAllExistingBuildsForJob
|
||||
|
||||
@ -8,7 +9,7 @@ pipeline {
|
||||
options { timestamps() }
|
||||
|
||||
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}"
|
||||
BUILD_ID = "${env.BUILD_ID}-${env.JOB_NAME}"
|
||||
}
|
||||
|
20
build.gradle
20
build.gradle
@ -448,7 +448,7 @@ task detektBaseline(type: JavaExec) {
|
||||
main = "io.gitlab.arturbosch.detekt.cli.Main"
|
||||
classpath = configurations.detekt
|
||||
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 params = ['-i', input, '-c', config, '-b', baseline, '--create-baseline']
|
||||
args(params)
|
||||
@ -612,6 +612,14 @@ task allParallelSlowIntegrationTest(type: ParallelTestGroup) {
|
||||
memoryInGbPerFork 10
|
||||
distribute Distribution.CLASS
|
||||
}
|
||||
task allParallelSmokeTest(type: ParallelTestGroup) {
|
||||
testGroups "smokeTest"
|
||||
numberOfShards 4
|
||||
streamOutput true
|
||||
coresPerFork 6
|
||||
memoryInGbPerFork 10
|
||||
distribute Distribution.METHOD
|
||||
}
|
||||
task allParallelUnitTest(type: ParallelTestGroup) {
|
||||
testGroups "test"
|
||||
numberOfShards 10
|
||||
@ -623,7 +631,15 @@ task allParallelUnitTest(type: ParallelTestGroup) {
|
||||
task allParallelUnitAndIntegrationTest(type: ParallelTestGroup) {
|
||||
testGroups "test", "integrationTest"
|
||||
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
|
||||
memoryInGbPerFork 10
|
||||
distribute Distribution.CLASS
|
||||
|
@ -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("Tests to Run: ");
|
||||
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()));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private void allocateTestsToForks(@NotNull List<TestBucket> matchedTests) {
|
||||
matchedTests.forEach(matchedTestBucket -> {
|
||||
TestsForForkContainer smallestContainer = Collections.min(forkContainers, Comparator.comparing(TestsForForkContainer::getCurrentDuration));
|
||||
@ -69,10 +68,9 @@ public class BucketingAllocator {
|
||||
return allDiscoveredTests.stream().map(tuple -> {
|
||||
String testName = tuple.getFirst();
|
||||
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]
|
||||
List<Tuple2<String, Double>> matchingTests = allTestsFromCSV.stream().filter(testFromCSV -> testFromCSV.getFirst().startsWith(noAsterixName)).collect(Collectors.toList());
|
||||
return new TestBucket(task, testName, noAsterixName, matchingTests);
|
||||
List<Tuple2<String, Double>> matchingTests = allTestsFromCSV.stream().filter(testFromCSV -> testFromCSV.getFirst().startsWith(testName)).collect(Collectors.toList());
|
||||
return new TestBucket(task, testName, matchingTests);
|
||||
}).sorted(Comparator.comparing(TestBucket::getDuration).reversed()).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@ -81,22 +79,20 @@ public class BucketingAllocator {
|
||||
TestLister lister = source.getFirst();
|
||||
Object testTask = source.getSecond();
|
||||
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 {
|
||||
final Object testTask;
|
||||
final String nameWithAsterix;
|
||||
final String nameWithoutAsterix;
|
||||
final String testName;
|
||||
final List<Tuple2<String, Double>> foundTests;
|
||||
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.nameWithAsterix = nameWithAsterix;
|
||||
this.nameWithoutAsterix = nameWithoutAsterix;
|
||||
this.testName = testName;
|
||||
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() {
|
||||
@ -107,8 +103,7 @@ public class BucketingAllocator {
|
||||
public String toString() {
|
||||
return "TestBucket{" +
|
||||
"testTask=" + testTask +
|
||||
", nameWithAsterix='" + nameWithAsterix + '\'' +
|
||||
", nameWithoutAsterix='" + nameWithoutAsterix + '\'' +
|
||||
", nameWithAsterix='" + testName + '\'' +
|
||||
", foundTests=" + foundTests +
|
||||
", duration=" + duration +
|
||||
'}';
|
||||
@ -142,7 +137,7 @@ public class BucketingAllocator {
|
||||
}
|
||||
|
||||
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() {
|
||||
|
@ -41,8 +41,8 @@ public class BucketingAllocatorTask extends DefaultTask {
|
||||
this.dependsOn(source);
|
||||
}
|
||||
|
||||
public List<String> getTestsForForkAndTestTask(Integer fork, Test testTask) {
|
||||
return allocator.getTestsForForkAndTestTask(fork, testTask);
|
||||
public List<String> getTestIncludesForForkAndTestTask(Integer fork, Test testTask) {
|
||||
return allocator.getTestsForForkAndTestTask(fork, testTask).stream().map(t -> t + "*").collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
@ -56,11 +56,11 @@ public class BucketingAllocatorTask extends DefaultTask {
|
||||
String duration = "Duration(ms)";
|
||||
List<CSVRecord> records = CSVFormat.DEFAULT.withHeader().parse(reader).getRecords();
|
||||
return records.stream().map(record -> {
|
||||
try{
|
||||
try {
|
||||
String testName = record.get(name);
|
||||
String testDuration = record.get(duration);
|
||||
return new Tuple2<>(testName, Math.max(Double.parseDouble(testDuration), 10));
|
||||
}catch (IllegalArgumentException | IllegalStateException e){
|
||||
return new Tuple2<>(testName, Math.max(Double.parseDouble(testDuration), 1));
|
||||
} catch (IllegalArgumentException | IllegalStateException e) {
|
||||
return null;
|
||||
}
|
||||
}).filter(Objects::nonNull).sorted(Comparator.comparing(Tuple2::getFirst)).collect(Collectors.toList());
|
||||
|
@ -10,7 +10,6 @@ import org.gradle.api.tasks.testing.Test
|
||||
*/
|
||||
class DistributedTesting implements Plugin<Project> {
|
||||
|
||||
|
||||
static def getPropertyAsInt(Project proj, String property, Integer 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
|
||||
//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
|
||||
//4. after each completed test write its name to a file to keep track of what finished for restart purposes
|
||||
project.subprojects { Project subProject ->
|
||||
subProject.tasks.withType(Test) { Test task ->
|
||||
println "Evaluating ${task.getPath()}"
|
||||
@ -47,7 +47,6 @@ class DistributedTesting implements Plugin<Project> {
|
||||
if (!task.hasProperty("ignoreForDistribution")) {
|
||||
KubesTest parallelTestTask = generateParallelTestingTask(subProject, task, imageBuildingTask, providedTag)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -129,9 +128,23 @@ class DistributedTesting implements Plugin<Project> {
|
||||
maxHeapSize = "6g"
|
||||
doFirst {
|
||||
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)
|
||||
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,
|
||||
task)
|
||||
subProject.logger.info "got ${includes.size()} tests to include into testing task ${task.getPath()}"
|
||||
@ -141,10 +154,18 @@ class DistributedTesting implements Plugin<Project> {
|
||||
excludeTestsMatching "*"
|
||||
}
|
||||
|
||||
includes.removeAll(executedTests)
|
||||
|
||||
executedTests.forEach { exclude ->
|
||||
subProject.logger.info "excluding: $exclude for testing task ${task.getPath()}"
|
||||
excludeTestsMatching exclude
|
||||
}
|
||||
|
||||
includes.forEach { include ->
|
||||
subProject.logger.info "including: $include for testing task ${task.getPath()}"
|
||||
includeTestsMatching include
|
||||
}
|
||||
|
||||
failOnNoMatchingTests false
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
147
buildSrc/src/main/groovy/net/corda/testing/ImageBuilding.java
Normal file
147
buildSrc/src/main/groovy/net/corda/testing/ImageBuilding.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
|
||||
}
|
388
buildSrc/src/main/groovy/net/corda/testing/KubesTest.java
Normal file
388
buildSrc/src/main/groovy/net/corda/testing/KubesTest.java
Normal 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);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
@ -79,7 +79,7 @@ class ListTests extends DefaultTask implements TestLister {
|
||||
.collect { c -> (c.getSubclasses() + Collections.singletonList(c)) }
|
||||
.flatten()
|
||||
.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()
|
||||
.toSet()
|
||||
|
||||
@ -97,7 +97,7 @@ class ListTests extends DefaultTask implements TestLister {
|
||||
.getClassesWithMethodAnnotation("org.junit.Test")
|
||||
.collect { c -> (c.getSubclasses() + Collections.singletonList(c)) }
|
||||
.flatten()
|
||||
.collect { ClassInfo c -> c.name + "*" }.flatten()
|
||||
.collect { ClassInfo c -> c.name }.flatten()
|
||||
.toSet()
|
||||
this.allTests = results.stream().sorted().collect(Collectors.toList())
|
||||
break
|
||||
|
@ -1,41 +1,29 @@
|
||||
package net.corda.testing;
|
||||
|
||||
import io.fabric8.kubernetes.api.model.Pod;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class KubePodResult {
|
||||
|
||||
private final Pod createdPod;
|
||||
private final CompletableFuture<Void> waiter;
|
||||
private volatile Integer resultCode = 255;
|
||||
private final int resultCode;
|
||||
private final File output;
|
||||
private volatile Collection<File> binaryResults = Collections.emptyList();
|
||||
private final Collection<File> binaryResults;
|
||||
|
||||
KubePodResult(Pod createdPod, CompletableFuture<Void> waiter, File output) {
|
||||
this.createdPod = createdPod;
|
||||
this.waiter = waiter;
|
||||
public KubePodResult(int resultCode, File output, Collection<File> binaryResults) {
|
||||
this.resultCode = resultCode;
|
||||
this.output = output;
|
||||
this.binaryResults = binaryResults;
|
||||
}
|
||||
|
||||
public void setResultCode(Integer code) {
|
||||
synchronized (createdPod) {
|
||||
this.resultCode = code;
|
||||
}
|
||||
}
|
||||
|
||||
public Integer getResultCode() {
|
||||
synchronized (createdPod) {
|
||||
return this.resultCode;
|
||||
}
|
||||
public int getResultCode() {
|
||||
return resultCode;
|
||||
}
|
||||
|
||||
public File getOutput() {
|
||||
synchronized (createdPod) {
|
||||
return output;
|
||||
}
|
||||
return output;
|
||||
}
|
||||
};
|
||||
|
||||
public Collection<File> getBinaryResults() {
|
||||
return binaryResults;
|
||||
}
|
||||
}
|
||||
|
@ -152,10 +152,10 @@ public class KubesReporting extends DefaultTask {
|
||||
if (!containersWithNonZeroReturnCodes.isEmpty()) {
|
||||
String reportUrl = new ConsoleRenderer().asClickableFileUrl(new File(destinationDir, "index.html"));
|
||||
if (shouldPrintOutput){
|
||||
containersWithNonZeroReturnCodes.forEach(container -> {
|
||||
containersWithNonZeroReturnCodes.forEach(podResult -> {
|
||||
try {
|
||||
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");
|
||||
} catch (IOException ignored) {
|
||||
}
|
||||
|
48
buildSrc/src/main/java/net/corda/testing/retry/Retry.java
Normal file
48
buildSrc/src/main/java/net/corda/testing/retry/Retry.java
Normal 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);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -21,12 +21,12 @@ public class BucketingAllocatorTest {
|
||||
BucketingAllocator bucketingAllocator = new BucketingAllocator(1, Collections::emptyList);
|
||||
|
||||
Object task = new Object();
|
||||
bucketingAllocator.addSource(() -> Arrays.asList("SomeTestingClass*", "AnotherTestingClass*"), task);
|
||||
bucketingAllocator.addSource(() -> Arrays.asList("SomeTestingClass", "AnotherTestingClass"), task);
|
||||
|
||||
bucketingAllocator.generateTestPlan();
|
||||
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);
|
||||
|
||||
Object task = new Object();
|
||||
bucketingAllocator.addSource(() -> Arrays.asList("SomeTestingClass*", "AnotherTestingClass*"), task);
|
||||
bucketingAllocator.addSource(() -> Arrays.asList("SomeTestingClass", "AnotherTestingClass"), task);
|
||||
|
||||
bucketingAllocator.generateTestPlan();
|
||||
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());
|
||||
|
||||
Assert.assertThat(allTests, IsIterableContainingInAnyOrder.containsInAnyOrder("SomeTestingClass*", "AnotherTestingClass*"));
|
||||
Assert.assertThat(allTests, IsIterableContainingInAnyOrder.containsInAnyOrder("SomeTestingClass", "AnotherTestingClass"));
|
||||
|
||||
}
|
||||
}
|
@ -51,6 +51,7 @@ import net.corda.core.utilities.toHexString
|
||||
import net.corda.serialization.internal.AllWhitelist
|
||||
import net.corda.serialization.internal.amqp.SerializerFactoryBuilder
|
||||
import net.corda.serialization.internal.amqp.hasCordaSerializable
|
||||
import net.corda.serialization.internal.amqp.registerCustomSerializers
|
||||
import java.math.BigDecimal
|
||||
import java.security.PublicKey
|
||||
import java.security.cert.CertPath
|
||||
@ -116,8 +117,10 @@ private class CordaSerializableClassIntrospector(private val context: Module.Set
|
||||
}
|
||||
|
||||
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.
|
||||
private val serializerFactory = SerializerFactoryBuilder.build(AllWhitelist, javaClass.classLoader)
|
||||
// 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).also {
|
||||
registerCustomSerializers(it)
|
||||
}
|
||||
|
||||
override fun changeProperties(config: SerializationConfig,
|
||||
beanDesc: BeanDescription,
|
||||
@ -125,7 +128,9 @@ private class CordaSerializableBeanSerializerModifier : BeanSerializerModifier()
|
||||
val beanClass = beanDesc.beanClass
|
||||
if (hasCordaSerializable(beanClass) && !SerializeAsToken::class.java.isAssignableFrom(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 }
|
||||
}
|
||||
return beanProperties
|
||||
|
@ -3,10 +3,15 @@ package net.corda.client.jfx.model
|
||||
import javafx.beans.property.SimpleObjectProperty
|
||||
import net.corda.client.rpc.CordaRPCClient
|
||||
import net.corda.client.rpc.CordaRPCConnection
|
||||
import net.corda.client.rpc.GracefulReconnect
|
||||
import net.corda.core.contracts.ContractState
|
||||
import net.corda.core.flows.StateMachineRunId
|
||||
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.Vault
|
||||
import net.corda.core.node.services.vault.DEFAULT_PAGE_NUM
|
||||
@ -72,7 +77,7 @@ class NodeMonitorModel : AutoCloseable {
|
||||
* TODO provide an unsubscribe mechanism
|
||||
*/
|
||||
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
|
||||
|
||||
// Vault snapshot (force single page load with MAX_PAGE_SIZE) + updates
|
||||
|
@ -4,6 +4,7 @@ import net.corda.client.rpc.CordaRPCClient
|
||||
import net.corda.client.rpc.CordaRPCClientConfiguration
|
||||
import net.corda.client.rpc.CordaRPCClientTest
|
||||
import net.corda.client.rpc.GracefulReconnect
|
||||
import net.corda.client.rpc.MaxRpcRetryException
|
||||
import net.corda.client.rpc.internal.ReconnectingCordaRPCOps
|
||||
import net.corda.core.messaging.startTrackedFlow
|
||||
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.internal.FINANCE_CORDAPPS
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||
import org.junit.Test
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.concurrent.thread
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
@ -39,7 +42,7 @@ class CordaRPCClientReconnectionTest {
|
||||
|
||||
@Test
|
||||
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 address = NetworkHostAndPort("localhost", portAllocator.nextPort())
|
||||
|
||||
@ -52,9 +55,7 @@ class CordaRPCClientReconnectionTest {
|
||||
}
|
||||
|
||||
val node = startNode()
|
||||
val client = CordaRPCClient(node.rpcAddress, CordaRPCClientConfiguration.DEFAULT.copy(
|
||||
maxReconnectAttempts = 5
|
||||
))
|
||||
val client = CordaRPCClient(node.rpcAddress)
|
||||
|
||||
(client.start(rpcUser.username, rpcUser.password, gracefulReconnect = gracefulReconnect)).use {
|
||||
val rpcOps = it.proxy as ReconnectingCordaRPCOps
|
||||
@ -79,7 +80,7 @@ class CordaRPCClientReconnectionTest {
|
||||
|
||||
@Test
|
||||
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 address = NetworkHostAndPort("localhost", portAllocator.nextPort())
|
||||
|
||||
@ -92,9 +93,7 @@ class CordaRPCClientReconnectionTest {
|
||||
}
|
||||
|
||||
val node = startNode()
|
||||
val client = CordaRPCClient(node.rpcAddress, CordaRPCClientConfiguration.DEFAULT.copy(
|
||||
maxReconnectAttempts = 5
|
||||
))
|
||||
val client = CordaRPCClient(node.rpcAddress)
|
||||
|
||||
(client.start(rpcUser.username, rpcUser.password, gracefulReconnect = gracefulReconnect)).use {
|
||||
val rpcOps = it.proxy as ReconnectingCordaRPCOps
|
||||
@ -119,7 +118,7 @@ class CordaRPCClientReconnectionTest {
|
||||
|
||||
@Test
|
||||
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)
|
||||
|
||||
fun startNode(address: NetworkHostAndPort): NodeHandle {
|
||||
@ -133,9 +132,7 @@ class CordaRPCClientReconnectionTest {
|
||||
val addresses = listOf(NetworkHostAndPort("localhost", portAllocator.nextPort()), NetworkHostAndPort("localhost", portAllocator.nextPort()))
|
||||
|
||||
val node = startNode(addresses[0])
|
||||
val client = CordaRPCClient(addresses, CordaRPCClientConfiguration.DEFAULT.copy(
|
||||
maxReconnectAttempts = 5
|
||||
))
|
||||
val client = CordaRPCClient(addresses)
|
||||
|
||||
(client.start(rpcUser.username, rpcUser.password, gracefulReconnect = gracefulReconnect)).use {
|
||||
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)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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 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 = {}) {
|
||||
constructor(onDisconnect: Runnable, onReconnect: Runnable ) :
|
||||
this(onDisconnect = { onDisconnect.run() }, onReconnect = { onReconnect.run() })
|
||||
class GracefulReconnect(val onDisconnect: () -> Unit = {}, val onReconnect: () -> Unit = {}, val maxAttempts: Int = 5) {
|
||||
@JvmOverloads
|
||||
constructor(onDisconnect: Runnable, onReconnect: Runnable, maxAttempts: Int = 5) :
|
||||
this(onDisconnect = { onDisconnect.run() }, onReconnect = { onReconnect.run() }, maxAttempts = maxAttempts)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -9,6 +9,14 @@ open class RPCException(message: String?, cause: Throwable?) : CordaRuntimeExcep
|
||||
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.
|
||||
*/
|
||||
|
@ -55,7 +55,7 @@ class ReconnectingCordaRPCOps private constructor(
|
||||
username: String,
|
||||
password: String,
|
||||
rpcConfiguration: CordaRPCClientConfiguration,
|
||||
gracefulReconnect: GracefulReconnect? = null,
|
||||
gracefulReconnect: GracefulReconnect = GracefulReconnect(),
|
||||
sslConfiguration: ClientRpcSslOptions? = null,
|
||||
classLoader: ClassLoader? = null,
|
||||
observersPool: ExecutorService
|
||||
@ -117,7 +117,7 @@ class ReconnectingCordaRPCOps private constructor(
|
||||
val rpcConfiguration: CordaRPCClientConfiguration,
|
||||
val sslConfiguration: ClientRpcSslOptions? = null,
|
||||
val classLoader: ClassLoader?,
|
||||
val gracefulReconnect: GracefulReconnect? = null,
|
||||
val gracefulReconnect: GracefulReconnect = GracefulReconnect(),
|
||||
val observersPool: ExecutorService
|
||||
) : RPCConnection<CordaRPCOps> {
|
||||
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
|
||||
// that they're only called once per reconnect.
|
||||
currentState = DIED
|
||||
gracefulReconnect?.onDisconnect?.invoke()
|
||||
gracefulReconnect.onDisconnect.invoke()
|
||||
//TODO - handle error cases
|
||||
log.error("Reconnecting to ${this.nodeHostAndPorts} due to error: ${e.message}")
|
||||
log.debug("", e)
|
||||
connect()
|
||||
previousConnection?.forceClose()
|
||||
gracefulReconnect?.onReconnect?.invoke()
|
||||
gracefulReconnect.onReconnect.invoke()
|
||||
}
|
||||
/**
|
||||
* 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
|
||||
while (true) {
|
||||
/**
|
||||
* This method retries the invoked operation in a loop by re-establishing the connection when there is a problem
|
||||
* 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 {
|
||||
log.debug { "Invoking RPC $method..." }
|
||||
return method.invoke(reconnectingRPCConnection.proxy, *(args ?: emptyArray())).also {
|
||||
@ -280,15 +287,20 @@ class ReconnectingCordaRPCOps private constructor(
|
||||
checkIfIsStartFlow(method, e)
|
||||
}
|
||||
}
|
||||
lastException = e.targetException
|
||||
remainingAttempts--
|
||||
}
|
||||
}
|
||||
|
||||
throw MaxRpcRetryException(maxNumberOfAttempts, lastException)
|
||||
}
|
||||
|
||||
override fun invoke(proxy: Any, method: Method, args: Array<out Any>?): Any? {
|
||||
return when (method.returnType) {
|
||||
DataFeed::class.java -> {
|
||||
// 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) {
|
||||
// This handles reconnecting and creates new feeds.
|
||||
uncheckedCast(this.invoke(reconnectingRPCConnection.proxy, method, args))
|
||||
@ -296,7 +308,7 @@ class ReconnectingCordaRPCOps private constructor(
|
||||
initialFeed.copy(updates = observable)
|
||||
}
|
||||
// TODO - add handlers for Observable return types.
|
||||
else -> doInvoke(method, args)
|
||||
else -> doInvoke(method, args, reconnectingRPCConnection.gracefulReconnect.maxAttempts)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,8 @@ import net.corda.core.CordaInternal
|
||||
import net.corda.core.DeleteForDJVM
|
||||
import net.corda.core.contracts.StateRef
|
||||
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.PartyAndCertificate
|
||||
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.
|
||||
*/
|
||||
@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
|
||||
* that this function does not communicate in itself, the counter-flow will be kicked off by the first send/receive.
|
||||
*/
|
||||
@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
|
||||
|
@ -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.
|
||||
* 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 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.
|
||||
|
@ -18,7 +18,7 @@ interface FlowStateMachine<FLOWRETURN> {
|
||||
fun <SUSPENDRETURN : Any> suspend(ioRequest: FlowIORequest<SUSPENDRETURN>, maySkipCheckpoint: Boolean): SUSPENDRETURN
|
||||
|
||||
@Suspendable
|
||||
fun initiateFlow(destination: Destination): FlowSession
|
||||
fun initiateFlow(destination: Destination, wellKnownParty: Party): FlowSession
|
||||
|
||||
fun checkFlowPermission(permissionName: String, extraAuditData: Map<String, String>)
|
||||
|
||||
|
@ -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>>
|
||||
get() = serializationEnvFields.mapNotNull { it.get()?.let { env -> Pair(it.name, env) } }
|
||||
@ -94,7 +99,8 @@ val effectiveSerializationEnv: SerializationEnvironment
|
||||
get() {
|
||||
return _allEnabledSerializationEnvs.let {
|
||||
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 }}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
6
detekt-baseline-config.yml
Normal file
6
detekt-baseline-config.yml
Normal 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
|
@ -691,8 +691,6 @@
|
||||
<ID>LongParameterList:Driver.kt$DriverParameters$( 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, cordappsForAllNodes: Set<TestCordapp>? )</ID>
|
||||
<ID>LongParameterList:DriverDSL.kt$DriverDSL$( defaultParameters: NodeParameters = NodeParameters(), providedName: CordaX500Name? = defaultParameters.providedName, rpcUsers: List<User> = defaultParameters.rpcUsers, verifierType: VerifierType = defaultParameters.verifierType, customOverrides: Map<String, Any?> = 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<User> = defaultParameters.rpcUsers, verifierType: VerifierType = defaultParameters.verifierType, customOverrides: Map<String, Any?> = 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<String, String> = DriverParameters().systemProperties, useTestClock: Boolean = DriverParameters().useTestClock, startNodesInProcess: Boolean = DriverParameters().startNodesInProcess, extraCordappPackagesToScan: List<String> = @Suppress("DEPRECATION") DriverParameters().extraCordappPackagesToScan, waitForAllNodesToFinish: Boolean = DriverParameters().waitForAllNodesToFinish, notarySpecs: List<NotarySpec> = DriverParameters().notarySpecs, jmxPolicy: JmxPolicy = DriverParameters().jmxPolicy, networkParameters: NetworkParameters = DriverParameters().networkParameters, compatibilityZone: CompatibilityZoneParams? = null, notaryCustomOverrides: Map<String, Any?> = DriverParameters().notaryCustomOverrides, inMemoryDB: Boolean = DriverParameters().inMemoryDB, cordappsForAllNodes: Collection<TestCordappInternal>? = null, dsl: DriverDSLImpl.() -> A )</ID>
|
||||
<ID>LongParameterList:DriverDSLImpl.kt$DriverDSLImpl.Companion$( config: NodeConfig, quasarJarPath: String, debugPort: Int?, overriddenSystemProperties: Map<String, String>, maximumHeapSize: String, logLevelOverride: String?, vararg extraCmdLineFlag: String )</ID>
|
||||
<ID>LongParameterList:DummyFungibleContract.kt$DummyFungibleContract$(inputs: List<State>, outputs: List<State>, tx: LedgerTransaction, issueCommand: CommandWithParties<Commands.Issue>, 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<Currency> = 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>
|
||||
@ -729,8 +727,6 @@
|
||||
<ID>LongParameterList:ParametersUtilities.kt$( notaries: List<NotaryInfo> = 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<String, List<AttachmentId>> = emptyMap(), epoch: Int = 1, eventHorizon: Duration = 30.days, packageOwnership: Map<String, PublicKey> = emptyMap() )</ID>
|
||||
<ID>LongParameterList:PersistentUniquenessProvider.kt$PersistentUniquenessProvider$( states: List<StateRef>, txId: SecureHash, callerIdentity: Party, requestSignature: NotarisationRequestSignature, timeWindow: TimeWindow?, references: List<StateRef> )</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<String>, classPath: List<String> = defaultClassPath, workingDirectory: Path? = null, jdwpPort: Int? = null, extraJvmArguments: List<String> = emptyList(), maximumHeapSize: String? = null )</ID>
|
||||
<ID>LongParameterList:ProcessUtilities.kt$ProcessUtilities$( className: String, arguments: List<String>, classPath: List<String> = defaultClassPath, workingDirectory: Path? = null, jdwpPort: Int? = null, extraJvmArguments: List<String> = emptyList(), maximumHeapSize: String? = null )</ID>
|
||||
<ID>LongParameterList:QueryCriteria.kt$QueryCriteria.FungibleAssetQueryCriteria$( participants: List<AbstractParty>? = this.participants, owner: List<AbstractParty>? = this.owner, quantity: ColumnPredicate<Long>? = this.quantity, issuer: List<AbstractParty>? = this.issuer, issuerRef: List<OpaqueBytes>? = this.issuerRef, status: Vault.StateStatus = this.status, contractStateTypes: Set<Class<out ContractState>>? = this.contractStateTypes )</ID>
|
||||
<ID>LongParameterList:QueryCriteria.kt$QueryCriteria.FungibleAssetQueryCriteria$( participants: List<AbstractParty>? = this.participants, owner: List<AbstractParty>? = this.owner, quantity: ColumnPredicate<Long>? = this.quantity, issuer: List<AbstractParty>? = this.issuer, issuerRef: List<OpaqueBytes>? = this.issuerRef, status: Vault.StateStatus = this.status, contractStateTypes: Set<Class<out ContractState>>? = this.contractStateTypes, relevancyStatus: Vault.RelevancyStatus = this.relevancyStatus )</ID>
|
||||
<ID>LongParameterList:QueryCriteria.kt$QueryCriteria.LinearStateQueryCriteria$( participants: List<AbstractParty>? = this.participants, uuid: List<UUID>? = this.uuid, externalId: List<String>? = this.externalId, status: Vault.StateStatus = this.status, contractStateTypes: Set<Class<out ContractState>>? = 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<Class<out ContractState>>? = null, stateRefs: List<StateRef>? = null, notary: List<AbstractParty>? = null, softLockingCondition: SoftLockingCondition? = null, timeCondition: TimeCondition? = null, relevancyStatus: Vault.RelevancyStatus = Vault.RelevancyStatus.ALL, constraintTypes: Set<Vault.ConstraintInfo.Type> = emptySet(), constraints: Set<Vault.ConstraintInfo> = emptySet(), participants: List<AbstractParty>? = null, externalIds: List<UUID> = emptyList() )</ID>
|
||||
<ID>LongParameterList:QueryCriteria.kt$QueryCriteria.VaultQueryCriteria$( status: Vault.StateStatus = this.status, contractStateTypes: Set<Class<out ContractState>>? = this.contractStateTypes, stateRefs: List<StateRef>? = this.stateRefs, notary: List<AbstractParty>? = this.notary, softLockingCondition: SoftLockingCondition? = this.softLockingCondition, timeCondition: TimeCondition? = this.timeCondition )</ID>
|
||||
<ID>LongParameterList:RPCClient.kt$RPCClient$( rpcOpsClass: Class<I>, 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<String, String> = emptyMap(), useTestClock: Boolean = false, startNodesInProcess: Boolean = false, waitForNodesToFinish: Boolean = false, extraCordappPackagesToScan: List<String> = emptyList(), notarySpecs: List<NotarySpec> = emptyList(), externalTrace: Trace? = null, @Suppress("DEPRECATION") jmxPolicy: JmxPolicy = JmxPolicy(), networkParameters: NetworkParameters = testNetworkParameters(), notaryCustomOverrides: Map<String, Any?> = emptyMap(), inMemoryDB: Boolean = true, cordappsForAllNodes: Collection<TestCordappInternal>? = null, dsl: RPCDriverDSL.() -> A )</ID>
|
||||
<ID>LongParameterList:RPCDriver.kt$RPCDriverDSL$( rpcUser: User = rpcTestUser, nodeLegalName: CordaX500Name = fakeNodeLegalName, configuration: RPCServerConfiguration = RPCServerConfiguration.DEFAULT, listOps: List<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>
|
||||
@ -1320,11 +1315,8 @@
|
||||
<ID>MaxLineLength:AMQPClientSerializationScheme.kt$AMQPClientSerializationScheme.Companion$fun createSerializationEnv(classLoader: ClassLoader? = null, customSerializers: Set<SerializationCustomSerializer<*, *>> = emptySet(), serializationWhitelists: Set<SerializationWhitelist> = emptySet(), serializerFactoriesForContexts: MutableMap<SerializationFactoryCacheKey, SerializerFactory> = AccessOrderLinkedHashMap<SerializationFactoryCacheKey, SerializerFactory>(128).toSynchronised()): SerializationEnvironment</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:AMQPSerializationScheme.kt$AbstractAMQPSerializationScheme$@DeleteForDJVM constructor(cordapps: List<Cordapp>) : this(cordapps.customSerializers, cordapps.serializationWhitelists, AccessOrderLinkedHashMap<SerializationFactoryCacheKey, SerializerFactory>(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${ // 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 -> factory.registerExternal(CorDappCustomSerializer(customSerializer, factory)) } cordappCustomSerializers.forEach { customSerializer -> factory.registerExternal(CorDappCustomSerializer(customSerializer, factory)) } context.properties[ContextPropertyKeys.SERIALIZERS]?.apply { uncheckedCast<Any, List<CustomSerializer<out Any>>>(this).forEach { factory.register(it) } } }</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$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$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$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$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>
|
||||
@ -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$/** * 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: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:BootTests.kt$BootTests$val logFile = logFolder.list { it.filter { a -> a.isRegularFile() && a.fileName.toString().startsWith("node") }.findFirst().get() }</ID>
|
||||
<ID>MaxLineLength:BootstrapperView.kt$BootstrapperView$return Pair(mapOf(Constants.REGION_ARG_NAME to ChoiceDialog<Region>(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$// 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 <T : Any> Config.parseAs(clazz: KClass<T>, onUnknownKeys: ((Set<String>, logger: Logger) -> 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$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() && !CordaSystemUtils.getOsName().toLowerCase().contains("server"))</ID>
|
||||
<ID>MaxLineLength:ConfigUtilities.kt$fun Any?.toConfigValue(): ConfigValue</ID>
|
||||
<ID>MaxLineLength:ConfigUtilities.kt$inline fun <reified T : Any> Config.parseAs(noinline onUnknownKeys: ((Set<String>, logger: Logger) -> Unit) = UnknownConfigKeysPolicy.FAIL::handle): T</ID>
|
||||
@ -1899,8 +1888,6 @@
|
||||
<ID>MaxLineLength:ConstraintsUtils.kt$input.isAutomaticHashConstraint() || output.isAutomaticHashConstraint() -> 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 -> throw IllegalArgumentException("Illegal constraint: AutomaticPlaceholderConstraint.") input.isAutomaticHashConstraint() || output.isAutomaticHashConstraint() -> throw IllegalArgumentException("Illegal constraint: AutomaticHashConstraint.") // Transition to the same constraint. input == output -> true // You can't transition from the AlwaysAcceptAttachmentConstraint to anything else, as it could hide something illegal. input is AlwaysAcceptAttachmentConstraint && output !is AlwaysAcceptAttachmentConstraint -> 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 && output is HashAttachmentConstraint -> input == output // Anything (except the AlwaysAcceptAttachmentConstraint) can be transformed to a HashAttachmentConstraint. input !is HashAttachmentConstraint && output is HashAttachmentConstraint -> 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 && output is SignatureAttachmentConstraint -> input.key == output.key // HashAttachmentConstraint can be transformed to a SignatureAttachmentConstraint when hash constraint verification checking disabled. HashAttachmentConstraint.disableHashConstraints && input is HashAttachmentConstraint && output is SignatureAttachmentConstraint -> 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 && output is SignatureAttachmentConstraint -> attachment.signerKeys.isNotEmpty() && output.key.keys.containsAll(attachment.signerKeys) else -> false }</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$mockNet = InternalMockNetwork(networkSendManuallyPumped = false, threadPerNode = true, cordappsForAllNodes = listOf(enclosedCordapp()))</ID>
|
||||
<ID>MaxLineLength:ContractJarTestUtils.kt$ContractJarTestUtils$@JvmOverloads fun makeTestContractJar(workingDir: Path, contractNames: List<String>, 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 getNumber(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: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>
|
||||
@ -2096,9 +2082,8 @@
|
||||
<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 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<String, Any>)</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 -> logLevelOverride debugPort == null -> "INFO" else -> "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 -> exclude.any { token -> 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 -> logLevelOverride debugPort == null -> "INFO" else -> "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 -> exclude.any { token -> 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 <A> pollUntilNonNull(pollName: String, pollInterval: Duration = DEFAULT_POLL_INTERVAL, warnCount: Int = DEFAULT_WARN_COUNT, check: () -> A?): CordaFuture<A></ID>
|
||||
<ID>MaxLineLength:DriverDSLImpl.kt$InternalDriverDSL$ fun pollUntilTrue(pollName: String, pollInterval: Duration = DEFAULT_POLL_INTERVAL, warnCount: Int = DEFAULT_WARN_COUNT, check: () -> Boolean): CordaFuture<Unit></ID>
|
||||
<ID>MaxLineLength:DriverDSLImpl.kt$fun DriverDSL.startNode(providedName: CordaX500Name, devMode: Boolean, parameters: NodeParameters = NodeParameters()): CordaFuture<NodeHandle></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: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:DurationSerializer.kt$DurationSerializer : Proxy</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: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:EnumSetSerializer.kt$EnumSetSerializer : Proxy</ID>
|
||||
<ID>MaxLineLength:ErrorCodeLoggingTests.kt$ErrorCodeLoggingTests$val linesWithErrorCode = logFile.useLines { lines -> lines.filter { line -> line.contains("[errorCode=") }.filter { line -> line.contains("moreInformationAt=https://errors.corda.net/") }.toList() }</ID>
|
||||
<ID>MaxLineLength:ErrorCodeLoggingTests.kt$fun NodeHandle.logFile(): File</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, 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$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<Int, SessionMessage>.to(node: TestStartedNode): SessionTransfer</ID>
|
||||
<ID>MaxLineLength:FlowLogic.kt$FlowLogic$ @Suspendable @JvmOverloads open fun <R : Any> receiveAll(receiveType: Class<R>, sessions: List<FlowSession>, maySkipCheckpoint: Boolean = false): List<UntrustworthyData<R>></ID>
|
||||
<ID>MaxLineLength:FlowLogic.kt$FlowLogic$ @Suspendable @JvmOverloads open fun receiveAllMap(sessions: Map<FlowSession, Class<out Any>>, maySkipCheckpoint: Boolean = false): Map<FlowSession, UntrustworthyData<Any>></ID>
|
||||
<ID>MaxLineLength:FlowLogic.kt$FlowLogic$?:</ID>
|
||||
@ -2371,7 +2349,6 @@
|
||||
<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$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:InterestSwapRestAPI.kt$InterestRateSwapAPI</ID>
|
||||
<ID>MaxLineLength:InternalAccessTestHelpers.kt$fun <T> ifThrowsAppend(strToAppendFn: () -> String, block: () -> 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$internal inline</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<CustomSerializer<out Any>> = listOf(LocalDateSerializer(factory), LocalTimeSerializer(factory))</ID>
|
||||
<ID>MaxLineLength:LocalPropertyInformation.kt$LocalPropertyInformation.CalculatedProperty$data</ID>
|
||||
<ID>MaxLineLength:LocalPropertyInformation.kt$LocalPropertyInformation.ConstructorPairedProperty$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$private</ID>
|
||||
<ID>MaxLineLength:LocalSerializerFactory.kt$DefaultLocalSerializerFactory$private val serializersByActualAndDeclaredType: MutableMap<ActualAndDeclaredType, AMQPSerializer<Any>> = 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.AnArray$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$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: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<ContractState>( QueryCriteria.VaultQueryCriteria(Vault.StateStatus.ALL), PageSpecification(DEFAULT_PAGE_NUM, MAX_PAGE_SIZE) ) val unconsumedStates = statesSnapshot.states.filterIndexed { index, _ -> 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 -> 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 -> ProgressTrackingEvent.createStreamFromStateMachineInfo(stateMachine) } val futureProgressTrackerUpdates = stateMachineUpdatesSubject.map { stateMachineUpdate -> if (stateMachineUpdate is StateMachineUpdate.Added) { ProgressTrackingEvent.createStreamFromStateMachineInfo(stateMachineUpdate.stateMachineInfo) ?: Observable.empty<ProgressTrackingEvent>() } else { Observable.empty<ProgressTrackingEvent>() } } // 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<ContractState>( QueryCriteria.VaultQueryCriteria(Vault.StateStatus.ALL), PageSpecification(DEFAULT_PAGE_NUM, MAX_PAGE_SIZE) ) val unconsumedStates = statesSnapshot.states.filterIndexed { index, _ -> 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 -> 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 -> ProgressTrackingEvent.createStreamFromStateMachineInfo(stateMachine) } val futureProgressTrackerUpdates = stateMachineUpdatesSubject.map { stateMachineUpdate -> if (stateMachineUpdate is StateMachineUpdate.Added) { ProgressTrackingEvent.createStreamFromStateMachineInfo(stateMachineUpdate.stateMachineInfo) ?: Observable.empty<ProgressTrackingEvent>() } else { Observable.empty<ProgressTrackingEvent>() } } // 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_") -> 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$override fun bindWithConfig(nodeConfiguration: NodeConfiguration): BindableNamedCacheFactory</ID>
|
||||
@ -2970,15 +2941,10 @@
|
||||
<ID>MaxLineLength:ObservableUtilities.kt$ @Suppress("UNCHECKED_CAST") fun <K : Any, A : Any, B> ObservableList<out A>.associateByAggregation(toKey: (A) -> K, assemble: (K, A) -> B): ObservableMap<K, ObservableList<B>></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<Any, ObservableList<A?>>(this).filtered { t -> 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:OffsetDateTimeSerializer.kt$OffsetDateTimeSerializer : Proxy</ID>
|
||||
<ID>MaxLineLength:OffsetDateTimeSerializer.kt$OffsetDateTimeSerializer$override val additionalSerializers: Iterable<CustomSerializer<out Any>> = listOf(LocalDateTimeSerializer(factory), ZoneIdSerializer(factory))</ID>
|
||||
<ID>MaxLineLength:OffsetTimeSerializer.kt$OffsetTimeSerializer : Proxy</ID>
|
||||
<ID>MaxLineLength:OffsetTimeSerializer.kt$OffsetTimeSerializer$override val additionalSerializers: Iterable<CustomSerializer<out Any>> = listOf(LocalTimeSerializer(factory), ZoneIdSerializer(factory))</ID>
|
||||
<ID>MaxLineLength:OnLedgerAsset.kt$OnLedgerAsset.Companion$deriveState: (TransactionState<S>, Amount<Issued<T>>, AbstractParty) -> TransactionState<S></ID>
|
||||
<ID>MaxLineLength:OnLedgerAsset.kt$OnLedgerAsset.Companion$generateMoveCommand: () -> CommandData</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: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: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>
|
||||
@ -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<BigDecimal></ID>
|
||||
<ID>MaxLineLength:Perceivable.kt$fun interest(@Suppress("UNUSED_PARAMETER") amount: BigDecimal, @Suppress("UNUSED_PARAMETER") dayCountConvention: String, @Suppress("UNUSED_PARAMETER") interest: Perceivable<BigDecimal> /* todo - appropriate type */, @Suppress("UNUSED_PARAMETER") start: Perceivable<Instant>, @Suppress("UNUSED_PARAMETER") end: Perceivable<Instant>): Perceivable<BigDecimal></ID>
|
||||
<ID>MaxLineLength:Perceivable.kt$fun interest(@Suppress("UNUSED_PARAMETER") amount: BigDecimal, @Suppress("UNUSED_PARAMETER") dayCountConvention: String, @Suppress("UNUSED_PARAMETER") interest: Perceivable<BigDecimal> /* todo - appropriate type */, @Suppress("UNUSED_PARAMETER") start: String, @Suppress("UNUSED_PARAMETER") end: String): Perceivable<BigDecimal></ID>
|
||||
<ID>MaxLineLength:PeriodSerializer.kt$PeriodSerializer : Proxy</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: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 -> 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: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 > 1 }.map { it.key }}")</ID>
|
||||
<ID>MaxLineLength:ProgressTracker.kt$ProgressTracker.Step$private fun definitionLocation(): String</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 -> 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 > 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: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<String>(BasicHSMKeyManagementService.PersistentKey::publicKeyHash.name), key.toStringShort())</ID>
|
||||
<ID>MaxLineLength:PublicKeyToOwningIdentityCacheImpl.kt$PublicKeyToOwningIdentityCacheImpl$criteriaBuilder.equal(queryRoot.get<String>(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>
|
||||
@ -3144,8 +3107,6 @@
|
||||
<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 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: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>
|
||||
@ -3285,8 +3246,6 @@
|
||||
<ID>MaxLineLength:SerializationAPI.kt$SerializationFactory$abstract</ID>
|
||||
<ID>MaxLineLength:SerializationAPI.kt$context: SerializationContext = serializationFactory.defaultContext</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<SerializationEnvironment>("inheritableContextSerializationEnv") { stack -> stack.fold(false) { isAGlobalThreadBeingCreated, e -> isAGlobalThreadBeingCreated || (e.className == "io.netty.util.concurrent.GlobalEventExecutor" && e.methodName == "startThread") || (e.className == "java.util.concurrent.ForkJoinPool\$DefaultForkJoinWorkerThreadFactory" && e.methodName == "newThread") } }</ID>
|
||||
<ID>MaxLineLength:SerializationFactory.kt$SerializationFactory$abstract</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$override fun fromProxy(proxy: StackTraceElementProxy): StackTraceElement</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) && Throwable::class.java.isAssignableFrom(clazz)) { val typeInformation = factory.getTypeInformation(clazz) val constructor = typeInformation.constructor val params = constructor.parameters.map { parameter -> 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.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.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: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<CustomSerializer<out Any>> = 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<StateAndRef<ContractState>>, outputs: List<TransactionState<ContractState>>, commands: List<CommandWithParties<CommandData>>, attachments: List<Attachment>, id: SecureHash, notary: Party?, timeWindow: TimeWindow?, privacySalt: PrivacySalt, networkParameters: NetworkParameters, references: List<StateAndRef<ContractState>>, componentGroups: List<ComponentGroup>? = null, serializedInputs: List<SerializedStateAndRef>? = null, serializedReferences: List<SerializedStateAndRef>? = null, isAttachmentTrusted: (Attachment) -> Boolean )</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>
|
||||
@ -3875,64 +3826,6 @@
|
||||
<ID>NestedBlockDepth:StatusTransitions.kt$StatusTransitions$ fun verify(tx: LedgerTransaction)</ID>
|
||||
<ID>NestedBlockDepth:ThrowableSerializer.kt$ThrowableSerializer$override fun fromProxy(proxy: ThrowableProxy): Throwable</ID>
|
||||
<ID>NestedBlockDepth:TransactionVerifierServiceInternal.kt$Verifier$ private fun verifyConstraintsValidity(contractAttachmentsByContract: Map<ContractClassName, ContractAttachment>)</ID>
|
||||
<ID>ReturnCount:AbstractPartyDescriptor.kt$AbstractPartyDescriptor$override fun <X : Any> unwrap(value: AbstractParty?, type: Class<X>, options: WrapperOptions): X?</ID>
|
||||
<ID>ReturnCount:AbstractPartyDescriptor.kt$AbstractPartyDescriptor$override fun <X : Any> wrap(value: X?, options: WrapperOptions): AbstractParty?</ID>
|
||||
<ID>ReturnCount:Address.kt$Address.Companion$fun <ERROR> validFromRawValue(rawValue: String, mapError: (String) -> ERROR): Validated<Address, ERROR></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 <T> getArrayValue(clazz: Class<T>, 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<*>): 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 <= entropy < 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<FlowLogic<*>>, args: Map<String, Any?>): HashMap<KParameter, Any?>?</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<String>, out: RenderPrintWriter, context: InvocationContext<out Any>, 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<*>, declaredType: Type): AMQPSerializer<Any></ID>
|
||||
<ID>ReturnCount:LocalTypeInformationBuilder.kt$ private fun constructorForDeserialization(type: Type): KFunction<Any>?</ID>
|
||||
<ID>ReturnCount:LocalTypeInformationBuilder.kt$LocalTypeInformationBuilder$ private fun buildNonAtomic(rawType: Class<*>, type: Type, typeIdentifier: TypeIdentifier, typeParameterInformation: List<LocalTypeInformation>): 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<InMemoryMessagingNetwork.MessageTransfer>, block: Boolean): Pair<InMemoryMessagingNetwork.MessageTransfer, List<Handler>>?</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<String></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 <T : FungibleState<*>> tryLockFungibleStatesForSpending( lockId: UUID, eligibleStatesQuery: QueryCriteria, amount: Amount<*>, contractStateType: Class<out T> ): List<StateAndRef<T>></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<Boolean>): 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<out Any?>?): 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<*>) -> Boolean, serialClass: Class<*>?): 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<Checkpoint>, isAnyCheckpointPersisted: Boolean, isStartIdempotent: Boolean, initialDeduplicationHandler: DeduplicationHandler? ): Flow?</ID>
|
||||
<ID>ReturnCount:SpecificationTest.kt$SpecificationTest$fun parseMax(elements: List<Long>): Valid<Long></ID>
|
||||
<ID>ReturnCount:StandaloneShell.kt$StandaloneShell$override fun runProgram(): Int</ID>
|
||||
<ID>ReturnCount:TransactionBuilder.kt$TransactionBuilder$ private fun handleContract( contractClassName: ContractClassName, inputStates: List<TransactionState<ContractState>>?, outputStates: List<TransactionState<ContractState>>?, explicitContractAttachment: AttachmentId?, services: ServicesForResolution ): Pair<AttachmentId, List<TransactionState<ContractState>>?></ID>
|
||||
<ID>ReturnCount:TransactionUtils.kt$ fun <T : Any> deserialiseComponentGroup(componentGroups: List<ComponentGroup>, clazz: KClass<T>, groupEnum: ComponentGroupEnum, forceDeserialize: Boolean = false, factory: SerializationFactory = SerializationFactory.defaultFactory, context: SerializationContext = factory.defaultContext): List<T></ID>
|
||||
<ID>ReturnCount:TransitionExecutorImpl.kt$TransitionExecutorImpl$@Suspendable override fun executeTransition( fiber: FlowFiber, previousState: StateMachineState, event: Event, transition: TransitionResult, actionExecutor: ActionExecutor ): Pair<FlowContinuation, StateMachineState></ID>
|
||||
<ID>ReturnCount:TypeParameterUtils.kt$ private fun inferTypeVariables(actualClass: Class<*>, declaredClass: Class<*>, declaredType: ParameterizedType): Type?</ID>
|
||||
<ID>ReturnCount:Util.kt$fun <T> debugCompare(perLeft: Perceivable<T>, perRight: Perceivable<T>)</ID>
|
||||
<ID>ReturnCount:Util.kt$fun debugCompare(arrLeft: Arrangement, arrRight: Arrangement)</ID>
|
||||
<ID>SpreadOperator:AMQPSerializationScheme.kt$AbstractAMQPSerializationScheme$(*it.whitelist.toTypedArray())</ID>
|
||||
<ID>SpreadOperator:AbstractNode.kt$FlowStarterImpl$(logicType, *args)</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:DevCertificatesTest.kt$DevCertificatesTest$(*oldX509Certificates)</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$(*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>
|
||||
@ -4528,7 +4420,6 @@
|
||||
<ID>WildcardImport:AMQPClientSerializationScheme.kt$import net.corda.serialization.internal.*</ID>
|
||||
<ID>WildcardImport:AMQPClientSerializationScheme.kt$import net.corda.serialization.internal.amqp.*</ID>
|
||||
<ID>WildcardImport:AMQPRemoteTypeModel.kt$import net.corda.serialization.internal.model.*</ID>
|
||||
<ID>WildcardImport:AMQPSerializationScheme.kt$import net.corda.core.internal.*</ID>
|
||||
<ID>WildcardImport:AMQPSerializationScheme.kt$import net.corda.core.serialization.*</ID>
|
||||
<ID>WildcardImport:AMQPServerSerializationScheme.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:NodeInterestRatesTest.kt$import net.corda.testing.core.*</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:NodeRegistrationTest.kt$import javax.ws.rs.*</ID>
|
||||
<ID>WildcardImport:NodeSchedulerService.kt$import net.corda.core.internal.*</ID>
|
||||
|
@ -183,12 +183,6 @@ style:
|
||||
OptionalAbstractKeyword:
|
||||
active: true
|
||||
excludes: "**/buildSrc/**"
|
||||
ReturnCount:
|
||||
active: true
|
||||
excludes: "**/buildSrc/**"
|
||||
max: 2
|
||||
excludedFunctions: "equals"
|
||||
excludeReturnFromLambda: true
|
||||
SafeCast:
|
||||
active: true
|
||||
excludes: "**/buildSrc/**"
|
||||
|
@ -37,6 +37,7 @@ shadowJar {
|
||||
classifier = null
|
||||
version = null
|
||||
zip64 true
|
||||
exclude '**/Log4j2Plugins.dat'
|
||||
}
|
||||
|
||||
docker{
|
||||
|
@ -60,8 +60,7 @@ function downloadTestnetCerts() {
|
||||
: ${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'}
|
||||
: ${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} }" \
|
||||
-H 'Content-Type: application/json' \
|
||||
curl \
|
||||
-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"
|
||||
fi
|
||||
|
@ -27,6 +27,7 @@ We currently support the following placeholders; they get substituted with the c
|
||||
|
||||
```groovy
|
||||
"|corda_version|"
|
||||
"|corda_version_lower|"
|
||||
"|java_version|"
|
||||
"|kotlin_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`
|
||||
(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.
|
||||
|
@ -88,6 +88,9 @@ Unreleased
|
||||
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.
|
||||
|
||||
* 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:
|
||||
|
||||
Version 4.1
|
||||
|
@ -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.
|
||||
|
||||
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
|
||||
|
||||
.. 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 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() {
|
||||
GracefulReconnect gracefulReconnect = new GracefulReconnect(this::onDisconnect, this::onReconnect);
|
||||
GracefulReconnect gracefulReconnect = new GracefulReconnect(this::onDisconnect, this::onReconnect, 3);
|
||||
CordaRPCClient cordaClient = new CordaRPCClient(nodeRpcAddress);
|
||||
CordaRPCConnection cordaRpcOps = cordaClient.start(rpcUserName, rpcUserPassword, gracefulReconnect);
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ def cordaSourceReadReplace(app, docname, source):
|
||||
|
||||
corda_substitutions = {
|
||||
"|corda_version|" : constants_properties_dict["cordaVersion"],
|
||||
"|corda_version_lower|" : constants_properties_dict["cordaVersion"].lower(),
|
||||
"|java_version|" : "8u"+constants_properties_dict["java8MinUpdateVersion"],
|
||||
"|kotlin_version|" : constants_properties_dict["kotlinVersion"],
|
||||
"|gradle_plugins_version|" : constants_properties_dict["gradlePluginsVersion"],
|
||||
|
@ -39,6 +39,8 @@ To alter this behaviour, the ``on-unknown-config-keys`` command-line argument ca
|
||||
Overriding values from node.conf
|
||||
--------------------------------
|
||||
|
||||
.. _corda_configuration_file_overriding_config:
|
||||
|
||||
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).
|
||||
|
||||
@ -54,6 +56,11 @@ JVM options
|
||||
.. note:: If the same field is overriden by both an environment variable and system property, the system property
|
||||
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
|
||||
-------------------------
|
||||
|
||||
|
@ -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
|
||||
- Other contextual Flow information (eg. counterparty), if logging within a flow
|
||||
- `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
|
||||
- CorDapp name, if logging from within a CorDapp
|
||||
|
||||
|
@ -21,7 +21,7 @@ In this example, the certificates are stored at ``/home/user/cordaBase/certifica
|
||||
-v /path/to/cordapps:/opt/corda/cordapps \
|
||||
-p 10200:10200 \
|
||||
-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:
|
||||
|
||||
@ -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 \
|
||||
-p 10200:10200 \
|
||||
-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.
|
||||
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" \
|
||||
-v /home/user/docker/config:/etc/corda \
|
||||
-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.
|
||||
``$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 \
|
||||
-p 10200:10200 \
|
||||
-p 10201:10201 \
|
||||
corda/corda-zulu-4.4-snapshot:latest
|
||||
corda/corda-zulu-java1.8-|corda_version_lower|:latest
|
||||
|
||||
|
||||
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" \
|
||||
-v /home/user/docker/config:/etc/corda \
|
||||
-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:
|
||||
@ -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 \
|
||||
-p 10200:10200 \
|
||||
-p 10201:10201 \
|
||||
corda/corda-zulu-4.4-snapshot:latest
|
||||
corda/corda-zulu-java1.8-|corda_version_lower|:latest
|
||||
|
||||
|
@ -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
|
||||
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
|
||||
(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
|
||||
|
@ -195,20 +195,20 @@ parameters will be accepted without user input. The following parameters with th
|
||||
:start-after: DOCSTART 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:
|
||||
|
||||
.. sourcecode:: guess
|
||||
|
||||
...
|
||||
NetworkParameterAcceptanceSettings {
|
||||
networkParameterAcceptanceSettings {
|
||||
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
|
||||
``@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
|
||||
node configuration:
|
||||
@ -216,7 +216,7 @@ node configuration:
|
||||
.. sourcecode:: guess
|
||||
|
||||
...
|
||||
NetworkParameterAcceptanceSettings {
|
||||
networkParameterAcceptanceSettings {
|
||||
excludedAutoAcceptableParameters: ["packageOwnership"]
|
||||
}
|
||||
...
|
||||
|
@ -71,25 +71,41 @@ PARTY_NAME FK to NODE_INFO_PARTY_CERT
|
||||
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 will also be cleared on ``rpc.clearNetworkMapCache()``.
|
||||
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.
|
||||
============================== ==========================================================================================
|
||||
|
||||
|
||||
============================== ==========================================================================================
|
||||
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
|
||||
PK_HASH The public key
|
||||
NAME The x500 name.
|
||||
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.
|
||||
TRANSACTION_VALUE The binary representation of the 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.
|
||||
============================== ==========================================================================================
|
||||
ID Primary key
|
||||
EXTERNAL_ID External id
|
||||
PUBLIC_KEY_HASH Public key hash
|
||||
============================== ==========================================================================================
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -10,7 +10,6 @@ import org.junit.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
@Ignore
|
||||
class NodeRPCTests {
|
||||
private val CORDA_VERSION_REGEX = "\\d+(\\.\\d+)?(-\\w+)?".toRegex()
|
||||
private val CORDA_VENDOR = "Corda Open Source"
|
||||
@ -28,7 +27,6 @@ class NodeRPCTests {
|
||||
driver(DriverParameters(notarySpecs = emptyList(), cordappsForAllNodes = CORDAPPS, extraCordappPackagesToScan = emptyList())) {
|
||||
val nodeDiagnosticInfo = startNode().get().rpc.nodeDiagnosticInfo()
|
||||
assertTrue(nodeDiagnosticInfo.version.matches(CORDA_VERSION_REGEX))
|
||||
assertTrue(nodeDiagnosticInfo.revision.matches(HEXADECIMAL_REGEX))
|
||||
assertEquals(PLATFORM_VERSION, nodeDiagnosticInfo.platformVersion)
|
||||
assertEquals(CORDA_VENDOR, nodeDiagnosticInfo.vendor)
|
||||
nodeDiagnosticInfo.cordapps.forEach { println("${it.shortName} ${it.type}") }
|
||||
|
@ -24,4 +24,4 @@ class NodeStartupPerformanceTests {
|
||||
println(times.map { it / 1_000_000.0 })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package net.corda.node.services.config
|
||||
|
||||
import com.typesafe.config.Config
|
||||
import com.typesafe.config.ConfigException
|
||||
import com.typesafe.config.ConfigFactory
|
||||
import com.typesafe.config.ConfigParseOptions
|
||||
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.div
|
||||
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.config.FileBasedCertificateStoreSupplier
|
||||
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.registerDevP2pCertificates
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.lang.IllegalStateException
|
||||
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))
|
||||
operator fun Config.plus(overrides: Map<String, Any?>): Config = ConfigFactory.parseMap(overrides).withFallback(this)
|
||||
@ -62,9 +67,69 @@ object ConfigHelper {
|
||||
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 {
|
||||
|
||||
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) })
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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")
|
@ -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
|
||||
* 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.
|
||||
|
@ -17,10 +17,11 @@ import net.corda.core.utilities.UntrustworthyData
|
||||
|
||||
class FlowSessionImpl(
|
||||
override val destination: Destination,
|
||||
private val wellKnownParty: Party,
|
||||
val sourceSessionId: SessionId
|
||||
) : 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)"
|
||||
|
||||
|
@ -355,10 +355,10 @@ class FlowStateMachineImpl<R>(override val id: StateMachineRunId,
|
||||
}
|
||||
|
||||
@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}" }
|
||||
val resume = processEventImmediately(
|
||||
Event.InitiateFlow(destination),
|
||||
Event.InitiateFlow(destination, wellKnownParty),
|
||||
isDbTransactionOpenOnEntry = true,
|
||||
isDbTransactionOpenOnExit = true
|
||||
) as FlowContinuation.Resume
|
||||
|
@ -466,7 +466,7 @@ class SingleThreadedStateMachineManager(
|
||||
try {
|
||||
val initiatedFlowFactory = getInitiatedFlowFactory(sessionMessage)
|
||||
val initiatedSessionId = SessionId.createRandom(secureRandom)
|
||||
val senderSession = FlowSessionImpl(sender, initiatedSessionId)
|
||||
val senderSession = FlowSessionImpl(sender, sender, initiatedSessionId)
|
||||
val flowLogic = initiatedFlowFactory.createFlow(senderSession)
|
||||
val initiatedFlowInfo = when (initiatedFlowFactory) {
|
||||
is InitiatedFlowFactory.Core -> FlowInfo(serviceHub.myInfo.platformVersion, "corda")
|
||||
|
@ -1,9 +1,13 @@
|
||||
package net.corda.node.services.statemachine
|
||||
|
||||
import net.corda.core.crypto.newSecureRandom
|
||||
import net.corda.core.flows.FlowException
|
||||
import net.corda.core.flows.ReceiveFinalityFlow
|
||||
import net.corda.core.flows.ReceiveTransactionFlow
|
||||
import net.corda.core.flows.StateMachineRunId
|
||||
import net.corda.core.flows.UnexpectedFlowEndException
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.DeclaredField
|
||||
import net.corda.core.internal.ThreadBox
|
||||
import net.corda.core.internal.TimedFlow
|
||||
import net.corda.core.internal.bufferUntilSubscribed
|
||||
@ -307,10 +311,19 @@ class StaffedFlowHospital(private val flowMessaging: FlowMessaging, private val
|
||||
|
||||
object FinalityDoctor : Staff {
|
||||
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 " +
|
||||
"the flow by re-starting the node. State machine state: $currentState", newError)
|
||||
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 {
|
||||
Diagnosis.NOT_MY_SPECIALTY
|
||||
}
|
||||
@ -319,6 +332,36 @@ class StaffedFlowHospital(private val flowMessaging: FlowMessaging, private val
|
||||
private fun isFromReceiveFinalityFlow(throwable: Throwable): Boolean {
|
||||
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!! )
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -235,7 +235,7 @@ class TopLevelTransition(
|
||||
return@builder FlowContinuation.ProcessEvents
|
||||
}
|
||||
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()))
|
||||
currentState = currentState.copy(checkpoint = checkpoint.copy(sessions = newSessions))
|
||||
actions.add(Action.AddSessionBinding(context.id, sourceSessionId))
|
||||
|
@ -69,8 +69,8 @@ class FlowFrameworkTests {
|
||||
@Before
|
||||
fun setUpMockNet() {
|
||||
mockNet = InternalMockNetwork(
|
||||
cordappsForAllNodes = listOf(DUMMY_CONTRACTS_CORDAPP),
|
||||
servicePeerAllocationStrategy = RoundRobin()
|
||||
cordappsForAllNodes = listOf(DUMMY_CONTRACTS_CORDAPP),
|
||||
servicePeerAllocationStrategy = RoundRobin()
|
||||
)
|
||||
|
||||
aliceNode = mockNet.createNode(InternalMockNodeParameters(legalName = ALICE_NAME))
|
||||
@ -139,13 +139,13 @@ class FlowFrameworkTests {
|
||||
mockNet.runNetwork()
|
||||
|
||||
assertSessionTransfers(
|
||||
aliceNode sent sessionInit(PingPongFlow::class, payload = 10L) to bobNode,
|
||||
bobNode sent sessionConfirm() to aliceNode,
|
||||
bobNode sent sessionData(20L) to aliceNode,
|
||||
aliceNode sent sessionData(11L) to bobNode,
|
||||
bobNode sent sessionData(21L) to aliceNode,
|
||||
aliceNode sent normalEnd to bobNode,
|
||||
bobNode sent normalEnd to aliceNode
|
||||
aliceNode sent sessionInit(PingPongFlow::class, payload = 10L) to bobNode,
|
||||
bobNode sent sessionConfirm() to aliceNode,
|
||||
bobNode sent sessionData(20L) to aliceNode,
|
||||
aliceNode sent sessionData(11L) to bobNode,
|
||||
bobNode sent sessionData(21L) to aliceNode,
|
||||
aliceNode sent normalEnd to bobNode,
|
||||
bobNode sent normalEnd to aliceNode
|
||||
)
|
||||
}
|
||||
|
||||
@ -167,7 +167,8 @@ class FlowFrameworkTests {
|
||||
it.message is ExistingSessionMessage && it.message.payload === EndSessionMessage
|
||||
}.subscribe { sessionEndReceived.release() }
|
||||
val resultFuture = aliceNode.services.startFlow(
|
||||
WaitForOtherSideEndBeforeSendAndReceive(bob, sessionEndReceived)).resultFuture
|
||||
WaitForOtherSideEndBeforeSendAndReceive(bob, sessionEndReceived)
|
||||
).resultFuture
|
||||
mockNet.runNetwork()
|
||||
assertThatExceptionOfType(UnexpectedFlowEndException::class.java).isThrownBy {
|
||||
resultFuture.getOrThrow()
|
||||
@ -186,10 +187,10 @@ class FlowFrameworkTests {
|
||||
mockNet.runNetwork()
|
||||
|
||||
assertThatExceptionOfType(MyFlowException::class.java)
|
||||
.isThrownBy { receivingFiber.resultFuture.getOrThrow() }
|
||||
.withMessage("Nothing useful")
|
||||
.withStackTraceContaining(ReceiveFlow::class.java.name) // Make sure the stack trace is that of the receiving flow
|
||||
.withStackTraceContaining("Received counter-flow exception from peer")
|
||||
.isThrownBy { receivingFiber.resultFuture.getOrThrow() }
|
||||
.withMessage("Nothing useful")
|
||||
.withStackTraceContaining(ReceiveFlow::class.java.name) // Make sure the stack trace is that of the receiving flow
|
||||
.withStackTraceContaining("Received counter-flow exception from peer")
|
||||
bobNode.database.transaction {
|
||||
assertThat(bobNode.internals.checkpointStorage.checkpoints()).isEmpty()
|
||||
}
|
||||
@ -197,15 +198,15 @@ class FlowFrameworkTests {
|
||||
assertThat(receivingFiber.state).isEqualTo(Strand.State.WAITING)
|
||||
assertThat((erroringFlow.get().stateMachine as FlowStateMachineImpl).state).isEqualTo(Strand.State.WAITING)
|
||||
assertThat(erroringFlowSteps.get()).containsExactly(
|
||||
Notification.createOnNext(ProgressTracker.STARTING),
|
||||
Notification.createOnNext(ExceptionFlow.START_STEP),
|
||||
Notification.createOnError(erroringFlow.get().exceptionThrown)
|
||||
Notification.createOnNext(ProgressTracker.STARTING),
|
||||
Notification.createOnNext(ExceptionFlow.START_STEP),
|
||||
Notification.createOnError(erroringFlow.get().exceptionThrown)
|
||||
)
|
||||
|
||||
assertSessionTransfers(
|
||||
aliceNode sent sessionInit(ReceiveFlow::class) to bobNode,
|
||||
bobNode sent sessionConfirm() to aliceNode,
|
||||
bobNode sent errorMessage(erroringFlow.get().exceptionThrown) to aliceNode
|
||||
aliceNode sent sessionInit(ReceiveFlow::class) to bobNode,
|
||||
bobNode sent sessionConfirm() to aliceNode,
|
||||
bobNode sent errorMessage(erroringFlow.get().exceptionThrown) to aliceNode
|
||||
)
|
||||
// Make sure the original stack trace isn't sent down the wire
|
||||
val lastMessage = receivedSessionMessages.last().message as ExistingSessionMessage
|
||||
@ -296,8 +297,8 @@ class FlowFrameworkTests {
|
||||
@Test
|
||||
fun waitForLedgerCommit() {
|
||||
val ptx = TransactionBuilder(notary = notaryIdentity)
|
||||
.addOutputState(DummyState(), DummyContract.PROGRAM_ID)
|
||||
.addCommand(dummyCommand(alice.owningKey))
|
||||
.addOutputState(DummyState(), DummyContract.PROGRAM_ID)
|
||||
.addCommand(dummyCommand(alice.owningKey))
|
||||
val stx = aliceNode.services.signInitialTransaction(ptx)
|
||||
|
||||
val committerStx = aliceNode.registerCordappFlowFactory(CommitterFlow::class) {
|
||||
@ -313,8 +314,8 @@ class FlowFrameworkTests {
|
||||
@Test
|
||||
fun `waitForLedgerCommit throws exception if any active session ends in error`() {
|
||||
val ptx = TransactionBuilder(notary = notaryIdentity)
|
||||
.addOutputState(DummyState(), DummyContract.PROGRAM_ID)
|
||||
.addCommand(dummyCommand())
|
||||
.addOutputState(DummyState(), DummyContract.PROGRAM_ID)
|
||||
.addCommand(dummyCommand())
|
||||
val stx = aliceNode.services.signInitialTransaction(ptx)
|
||||
|
||||
aliceNode.registerCordappFlowFactory(WaitForLedgerCommitFlow::class) { ExceptionFlow { throw Exception("Error") } }
|
||||
@ -354,8 +355,8 @@ class FlowFrameworkTests {
|
||||
val result = aliceNode.services.startFlow(UpgradedFlow(bob)).resultFuture
|
||||
mockNet.runNetwork()
|
||||
assertThat(receivedSessionMessages).startsWith(
|
||||
aliceNode sent sessionInit(UpgradedFlow::class, flowVersion = 2) to bobNode,
|
||||
bobNode sent sessionConfirm(flowVersion = 1) to aliceNode
|
||||
aliceNode sent sessionInit(UpgradedFlow::class, flowVersion = 2) to bobNode,
|
||||
bobNode sent sessionConfirm(flowVersion = 1) to aliceNode
|
||||
)
|
||||
val (receivedPayload, node2FlowVersion) = result.getOrThrow()
|
||||
assertThat(receivedPayload).isEqualTo("Old initiated")
|
||||
@ -369,8 +370,8 @@ class FlowFrameworkTests {
|
||||
val flowInfo = aliceNode.services.startFlow(initiatingFlow).resultFuture
|
||||
mockNet.runNetwork()
|
||||
assertThat(receivedSessionMessages).startsWith(
|
||||
aliceNode sent sessionInit(SendFlow::class, flowVersion = 1, payload = "Old initiating") to bobNode,
|
||||
bobNode sent sessionConfirm(flowVersion = 2) to aliceNode
|
||||
aliceNode sent sessionInit(SendFlow::class, flowVersion = 1, payload = "Old initiating") to bobNode,
|
||||
bobNode sent sessionConfirm(flowVersion = 2) to aliceNode
|
||||
)
|
||||
assertThat(flowInfo.get().flowVersion).isEqualTo(2)
|
||||
}
|
||||
@ -380,8 +381,8 @@ class FlowFrameworkTests {
|
||||
val future = aliceNode.services.startFlow(NeverRegisteredFlow("Hello", bob)).resultFuture
|
||||
mockNet.runNetwork()
|
||||
assertThatExceptionOfType(UnexpectedFlowEndException::class.java)
|
||||
.isThrownBy { future.getOrThrow() }
|
||||
.withMessageEndingWith("${NeverRegisteredFlow::class.java.name} is not registered")
|
||||
.isThrownBy { future.getOrThrow() }
|
||||
.withMessageEndingWith("${NeverRegisteredFlow::class.java.name} is not registered")
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -441,9 +442,9 @@ class FlowFrameworkTests {
|
||||
erroringFlowFuture.getOrThrow()
|
||||
val flowSteps = erroringFlowSteps.get()
|
||||
assertThat(flowSteps).containsExactly(
|
||||
Notification.createOnNext(ProgressTracker.STARTING),
|
||||
Notification.createOnNext(ExceptionFlow.START_STEP),
|
||||
Notification.createOnError(erroringFlowFuture.get().exceptionThrown)
|
||||
Notification.createOnNext(ProgressTracker.STARTING),
|
||||
Notification.createOnNext(ExceptionFlow.START_STEP),
|
||||
Notification.createOnError(erroringFlowFuture.get().exceptionThrown)
|
||||
)
|
||||
|
||||
val receiveFlowException = assertFailsWith(UnexpectedFlowEndException::class) {
|
||||
@ -451,28 +452,28 @@ class FlowFrameworkTests {
|
||||
}
|
||||
assertThat(receiveFlowException.message).doesNotContain("evil bug!")
|
||||
assertThat(receiveFlowSteps.get()).containsExactly(
|
||||
Notification.createOnNext(ProgressTracker.STARTING),
|
||||
Notification.createOnNext(ReceiveFlow.START_STEP),
|
||||
Notification.createOnError(receiveFlowException)
|
||||
Notification.createOnNext(ProgressTracker.STARTING),
|
||||
Notification.createOnNext(ReceiveFlow.START_STEP),
|
||||
Notification.createOnError(receiveFlowException)
|
||||
)
|
||||
|
||||
assertSessionTransfers(
|
||||
aliceNode sent sessionInit(ReceiveFlow::class) to bobNode,
|
||||
bobNode sent sessionConfirm() to aliceNode,
|
||||
bobNode sent errorMessage() to aliceNode
|
||||
aliceNode sent sessionInit(ReceiveFlow::class) to bobNode,
|
||||
bobNode sent sessionConfirm() to aliceNode,
|
||||
bobNode sent errorMessage() to aliceNode
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `initiating flow using unknown AnonymousParty`() {
|
||||
val anonymousBob = bobNode.services.keyManagementService.freshKeyAndCert(bobNode.info.legalIdentitiesAndCerts.single(), false)
|
||||
.party.anonymise()
|
||||
.party.anonymise()
|
||||
bobNode.registerCordappFlowFactory(SendAndReceiveFlow::class) { SingleInlinedSubFlow(it) }
|
||||
val result = aliceNode.services.startFlow(SendAndReceiveFlow(anonymousBob, "Hello")).resultFuture
|
||||
mockNet.runNetwork()
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy { result.getOrThrow() }
|
||||
.withMessage("We do not know who $anonymousBob belongs to")
|
||||
.isThrownBy { result.getOrThrow() }
|
||||
.withMessage("Could not resolve destination: $anonymousBob")
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -497,16 +498,18 @@ class FlowFrameworkTests {
|
||||
private val FlowLogic<*>.progressSteps: CordaFuture<List<Notification<ProgressTracker.Step>>>
|
||||
get() {
|
||||
return progressTracker!!.changes
|
||||
.ofType(Change.Position::class.java)
|
||||
.map { it.newStep }
|
||||
.materialize()
|
||||
.toList()
|
||||
.toFuture()
|
||||
.ofType(Change.Position::class.java)
|
||||
.map { it.newStep }
|
||||
.materialize()
|
||||
.toList()
|
||||
.toFuture()
|
||||
}
|
||||
|
||||
@InitiatingFlow
|
||||
private class WaitForOtherSideEndBeforeSendAndReceive(val otherParty: Party,
|
||||
@Transient val receivedOtherFlowEnd: Semaphore) : FlowLogic<Unit>() {
|
||||
private class WaitForOtherSideEndBeforeSendAndReceive(
|
||||
val otherParty: Party,
|
||||
@Transient val receivedOtherFlowEnd: Semaphore
|
||||
) : FlowLogic<Unit>() {
|
||||
@Suspendable
|
||||
override fun call() {
|
||||
// Kick off the flow on the other side ...
|
||||
@ -626,7 +629,8 @@ class FlowFrameworkTests {
|
||||
//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<*>> {
|
||||
return smm.findStateMachines(P::class.java).single()
|
||||
@ -637,17 +641,17 @@ private fun sanitise(message: SessionMessage) = when (message) {
|
||||
is ExistingSessionMessage -> {
|
||||
val payload = message.payload
|
||||
message.copy(
|
||||
recipientSessionId = SessionId(0),
|
||||
payload = when (payload) {
|
||||
is ConfirmSessionMessage -> payload.copy(
|
||||
initiatedSessionId = SessionId(0),
|
||||
initiatedFlowInfo = payload.initiatedFlowInfo.copy(appName = "")
|
||||
)
|
||||
is ErrorSessionMessage -> payload.copy(
|
||||
errorId = 0
|
||||
)
|
||||
else -> payload
|
||||
}
|
||||
recipientSessionId = SessionId(0),
|
||||
payload = when (payload) {
|
||||
is ConfirmSessionMessage -> payload.copy(
|
||||
initiatedSessionId = SessionId(0),
|
||||
initiatedFlowInfo = payload.initiatedFlowInfo.copy(appName = "")
|
||||
)
|
||||
is ErrorSessionMessage -> payload.copy(
|
||||
errorId = 0
|
||||
)
|
||||
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 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) {
|
||||
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()
|
||||
|
||||
@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)
|
||||
|
||||
@Suspendable
|
||||
@ -795,7 +805,8 @@ internal class SendAndReceiveFlow(private val destination: Destination, private
|
||||
}
|
||||
|
||||
@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)
|
||||
|
||||
@Transient
|
||||
|
@ -6,7 +6,8 @@ import net.corda.core.DeleteForDJVM
|
||||
import net.corda.core.KeepForDJVM
|
||||
import net.corda.core.StubOutForDJVM
|
||||
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.utilities.ByteSequence
|
||||
import net.corda.serialization.internal.CordaSerializationMagic
|
||||
@ -46,14 +47,20 @@ abstract class AbstractAMQPSerializationScheme(
|
||||
val sff: SerializerFactoryFactory = createSerializerFactoryFactory()
|
||||
) : SerializationScheme {
|
||||
@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.
|
||||
private val serializerFactoriesForContexts: MutableMap<SerializationFactoryCacheKey, SerializerFactory> = if (maybeNotConcurrentSerializerFactoriesForContexts is AccessOrderLinkedHashMap<*, *>) {
|
||||
Collections.synchronizedMap(maybeNotConcurrentSerializerFactoriesForContexts)
|
||||
} else {
|
||||
maybeNotConcurrentSerializerFactoriesForContexts
|
||||
}
|
||||
private val serializerFactoriesForContexts: MutableMap<SerializationFactoryCacheKey, SerializerFactory> =
|
||||
if (maybeNotConcurrentSerializerFactoriesForContexts is
|
||||
AccessOrderLinkedHashMap<SerializationFactoryCacheKey, SerializerFactory>) {
|
||||
Collections.synchronizedMap(maybeNotConcurrentSerializerFactoriesForContexts)
|
||||
} else {
|
||||
maybeNotConcurrentSerializerFactoriesForContexts
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val serializationWhitelists: List<SerializationWhitelist> by lazy { listOf(DefaultWhitelist) }
|
||||
@ -68,45 +75,16 @@ abstract class AbstractAMQPSerializationScheme(
|
||||
}
|
||||
|
||||
private fun registerCustomSerializers(context: SerializationContext, factory: SerializerFactory) {
|
||||
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)
|
||||
}
|
||||
factory.register(publicKeySerializer)
|
||||
registerCustomSerializers(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.
|
||||
// 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 ->
|
||||
factory.registerExternal(CorDappCustomSerializer(customSerializer, factory))
|
||||
}
|
||||
@ -126,22 +104,12 @@ abstract class AbstractAMQPSerializationScheme(
|
||||
factory.addToWhitelist(*it.whitelist.toTypedArray())
|
||||
}
|
||||
cordappSerializationWhitelists.forEach {
|
||||
it.whitelist.forEach {
|
||||
clazz -> factory.addToWhitelist(clazz)
|
||||
it.whitelist.forEach { 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 rpcServerSerializerFactory(context: SerializationContext): SerializerFactory
|
||||
|
||||
@ -189,3 +157,49 @@ abstract class AbstractAMQPSerializationScheme(
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
* 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 fromProxy(proxy: BitSetProxy): BitSet = BitSet.valueOf(proxy.bytes)
|
||||
|
@ -8,8 +8,13 @@ import java.security.cert.CertPath
|
||||
import java.security.cert.CertificateException
|
||||
import java.security.cert.CertificateFactory
|
||||
|
||||
class CertPathSerializer(factory: SerializerFactory)
|
||||
: CustomSerializer.Proxy<CertPath, CertPathSerializer.CertPathProxy>(CertPath::class.java, CertPathProxy::class.java, factory) {
|
||||
class CertPathSerializer(
|
||||
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 fromProxy(proxy: CertPathProxy): CertPath {
|
||||
|
@ -6,7 +6,6 @@ import net.corda.core.utilities.trace
|
||||
import net.corda.serialization.internal.amqp.AMQPNotSerializableException
|
||||
import net.corda.serialization.internal.amqp.CustomSerializer
|
||||
import net.corda.serialization.internal.amqp.LocalSerializerFactory
|
||||
import net.corda.serialization.internal.amqp.SerializerFactory
|
||||
import net.corda.serialization.internal.amqp.custom.ClassSerializer.ClassProxy
|
||||
|
||||
/**
|
||||
|
@ -15,16 +15,27 @@ import java.security.PublicKey
|
||||
* A serializer for [ContractAttachment] that uses a proxy object to write out the full attachment eagerly.
|
||||
* @param factory the serializerFactory
|
||||
*/
|
||||
class ContractAttachmentSerializer(factory: SerializerFactory) : CustomSerializer.Proxy<ContractAttachment,
|
||||
ContractAttachmentSerializer.ContractAttachmentProxy>(ContractAttachment::class.java,
|
||||
ContractAttachmentProxy::class.java, factory) {
|
||||
class ContractAttachmentSerializer(
|
||||
factory: SerializerFactory
|
||||
) : CustomSerializer.Proxy<ContractAttachment, ContractAttachmentSerializer.ContractAttachmentProxy>(
|
||||
ContractAttachment::class.java,
|
||||
ContractAttachmentProxy::class.java,
|
||||
factory
|
||||
) {
|
||||
override fun toProxy(obj: ContractAttachment): ContractAttachmentProxy {
|
||||
val bytes = try {
|
||||
obj.attachment.open().readFully()
|
||||
} catch (e: Exception) {
|
||||
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 {
|
||||
@ -32,5 +43,12 @@ class ContractAttachmentSerializer(factory: SerializerFactory) : CustomSerialize
|
||||
}
|
||||
|
||||
@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
|
||||
)
|
||||
}
|
@ -6,7 +6,10 @@ import java.util.*
|
||||
/**
|
||||
* 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,
|
||||
maker = { Currency.getInstance(it) },
|
||||
unmaker = { it.currencyCode })
|
||||
unmaker = { it.currencyCode }
|
||||
)
|
||||
|
@ -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.
|
||||
*/
|
||||
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 fromProxy(proxy: DurationProxy): Duration = Duration.ofSeconds(proxy.seconds, proxy.nanos.toLong())
|
||||
|
@ -10,7 +10,13 @@ import java.util.*
|
||||
/**
|
||||
* 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 fun toProxy(obj: EnumSet<*>): EnumSetProxy = EnumSetProxy(elementType(obj), obj.toList())
|
||||
|
@ -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].
|
||||
*/
|
||||
object InputStreamSerializer : CustomSerializer.Implements<InputStream>(InputStream::class.java) {
|
||||
object InputStreamSerializer
|
||||
: CustomSerializer.Implements<InputStream>(
|
||||
InputStream::class.java
|
||||
) {
|
||||
override val revealSubclassesInSchema: Boolean = true
|
||||
|
||||
override val schemaForDocumentation = Schema(
|
||||
|
@ -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.
|
||||
*/
|
||||
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 fromProxy(proxy: InstantProxy): Instant = Instant.ofEpochSecond(proxy.epochSeconds, proxy.nanos.toLong())
|
||||
|
@ -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.
|
||||
*/
|
||||
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 fromProxy(proxy: LocalDateProxy): LocalDate = LocalDate.of(proxy.year, proxy.month.toInt(), proxy.day.toInt())
|
||||
|
@ -10,8 +10,17 @@ import java.time.LocalTime
|
||||
/**
|
||||
* 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) {
|
||||
override val additionalSerializers: Iterable<CustomSerializer<out Any>> = listOf(LocalDateSerializer(factory), LocalTimeSerializer(factory))
|
||||
class LocalDateTimeSerializer(
|
||||
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())
|
||||
|
||||
|
@ -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.
|
||||
*/
|
||||
class LocalTimeSerializer(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)
|
||||
class LocalTimeSerializer(
|
||||
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
|
||||
data class LocalTimeProxy(val hour: Byte, val minute: Byte, val second: Byte, val nano: Int)
|
||||
|
@ -8,8 +8,9 @@ import java.time.MonthDay
|
||||
/**
|
||||
* A serializer for [MonthDay] that uses a proxy object to write out the integer form.
|
||||
*/
|
||||
class MonthDaySerializer(factory: SerializerFactory)
|
||||
: CustomSerializer.Proxy<MonthDay, MonthDaySerializer.MonthDayProxy>(
|
||||
class MonthDaySerializer(
|
||||
factory: SerializerFactory
|
||||
) : CustomSerializer.Proxy<MonthDay, MonthDaySerializer.MonthDayProxy>(
|
||||
MonthDay::class.java, MonthDayProxy::class.java, factory
|
||||
) {
|
||||
override fun toProxy(obj: MonthDay): MonthDayProxy = MonthDayProxy(obj.monthValue.toByte(), obj.dayOfMonth.toByte())
|
||||
|
@ -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.
|
||||
*/
|
||||
class OffsetDateTimeSerializer(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))
|
||||
class OffsetDateTimeSerializer(
|
||||
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)
|
||||
|
||||
|
@ -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.
|
||||
*/
|
||||
class OffsetTimeSerializer(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))
|
||||
class OffsetTimeSerializer(
|
||||
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)
|
||||
|
||||
|
@ -10,8 +10,13 @@ import net.corda.serialization.internal.amqp.SerializerFactory
|
||||
* to save on network bandwidth
|
||||
* Uses [OpaqueBytes] as a proxy
|
||||
*/
|
||||
class OpaqueBytesSubSequenceSerializer(factory: SerializerFactory) :
|
||||
CustomSerializer.Proxy<OpaqueBytesSubSequence, OpaqueBytes>(OpaqueBytesSubSequence::class.java, OpaqueBytes::class.java, factory) {
|
||||
class OpaqueBytesSubSequenceSerializer(
|
||||
factory: SerializerFactory
|
||||
) : CustomSerializer.Proxy<OpaqueBytesSubSequence, OpaqueBytes>(
|
||||
OpaqueBytesSubSequence::class.java,
|
||||
OpaqueBytes::class.java,
|
||||
factory
|
||||
) {
|
||||
override val additionalSerializers: Iterable<CustomSerializer<out Any>> = emptyList()
|
||||
override fun toProxy(obj: OpaqueBytesSubSequence): OpaqueBytes = OpaqueBytes(obj.copyBytes())
|
||||
override fun fromProxy(proxy: OpaqueBytes): OpaqueBytesSubSequence = OpaqueBytesSubSequence(proxy.bytes, proxy.offset, proxy.size)
|
||||
|
@ -3,13 +3,18 @@ package net.corda.serialization.internal.amqp.custom
|
||||
import net.corda.core.KeepForDJVM
|
||||
import net.corda.serialization.internal.amqp.CustomSerializer
|
||||
import net.corda.serialization.internal.amqp.SerializerFactory
|
||||
import java.time.OffsetTime
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
return OptionalProxy(obj.orElse(null))
|
||||
|
@ -8,7 +8,13 @@ import java.time.Period
|
||||
/**
|
||||
* 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 fromProxy(proxy: PeriodProxy): Period = Period.of(proxy.years, proxy.months, proxy.days)
|
||||
|
@ -9,9 +9,19 @@ import org.apache.qpid.proton.codec.Data
|
||||
import java.lang.reflect.Type
|
||||
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,
|
||||
context: SerializationContext
|
||||
|
@ -10,8 +10,18 @@ import java.security.PublicKey
|
||||
/**
|
||||
* A serializer that writes out a public key in X.509 format.
|
||||
*/
|
||||
object PublicKeySerializer : CustomSerializer.Implements<PublicKey>(PublicKey::class.java) {
|
||||
override val schemaForDocumentation = Schema(listOf(RestrictedType(type.toString(), "", listOf(type.toString()), AMQPTypeIdentifiers.primitiveTypeName(ByteArray::class.java), descriptor, emptyList())))
|
||||
object PublicKeySerializer
|
||||
: 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,
|
||||
context: SerializationContext
|
||||
|
@ -11,7 +11,13 @@ import net.corda.serialization.internal.model.LocalTypeInformation
|
||||
import java.io.NotSerializableException
|
||||
|
||||
@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 {
|
||||
private val logger = contextLogger()
|
||||
|
@ -7,7 +7,10 @@ import java.lang.reflect.Type
|
||||
import java.security.cert.CertificateFactory
|
||||
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(
|
||||
type.toString(),
|
||||
"",
|
||||
|
@ -7,7 +7,10 @@ import java.lang.reflect.Type
|
||||
import java.security.cert.CertificateFactory
|
||||
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(
|
||||
type.toString(),
|
||||
"",
|
||||
|
@ -8,7 +8,13 @@ import java.time.YearMonth
|
||||
/**
|
||||
* 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 fromProxy(proxy: YearMonthProxy): YearMonth = YearMonth.of(proxy.year, proxy.month.toInt())
|
||||
|
@ -8,7 +8,13 @@ import java.time.Year
|
||||
/**
|
||||
* 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 fromProxy(proxy: YearProxy): Year = Year.of(proxy.year)
|
||||
|
@ -8,7 +8,13 @@ import java.time.ZoneId
|
||||
/**
|
||||
* 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 fun toProxy(obj: ZoneId): ZoneIdProxy = ZoneIdProxy(obj.id)
|
||||
|
@ -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.
|
||||
*/
|
||||
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
|
||||
// so that any change to internals of `ZonedDateTime` is detected early.
|
||||
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 {
|
||||
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 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
|
||||
data class ZonedDateTimeProxy(val dateTime: LocalDateTime, val offset: ZoneOffset, val zone: ZoneId)
|
||||
|
@ -3,8 +3,17 @@ ENV GRADLE_USER_HOME=/tmp/gradle
|
||||
RUN mkdir /tmp/gradle && mkdir -p /home/root/.m2/repository
|
||||
|
||||
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 && \
|
||||
apt-get install -y java-common && dpkg -i 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 && apt install -y ./zulu8.40.0.25-ca-jdk8.0.222-linux_amd64.deb && \
|
||||
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
|
||||
|
||||
|
||||
|
@ -199,7 +199,8 @@ fun <A> driver(defaultParameters: DriverParameters = DriverParameters(), dsl: Dr
|
||||
networkParameters = defaultParameters.networkParameters,
|
||||
notaryCustomOverrides = defaultParameters.notaryCustomOverrides,
|
||||
inMemoryDB = defaultParameters.inMemoryDB,
|
||||
cordappsForAllNodes = uncheckedCast(defaultParameters.cordappsForAllNodes)
|
||||
cordappsForAllNodes = uncheckedCast(defaultParameters.cordappsForAllNodes),
|
||||
environmentVariables = defaultParameters.environmentVariables
|
||||
),
|
||||
coerce = { it },
|
||||
dsl = dsl
|
||||
@ -255,10 +256,46 @@ data class DriverParameters(
|
||||
val networkParameters: NetworkParameters = testNetworkParameters(notaries = emptyList()),
|
||||
val notaryCustomOverrides: Map<String, Any?> = emptyMap(),
|
||||
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(
|
||||
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(
|
||||
isDebug: Boolean = false,
|
||||
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 withInMemoryDB(inMemoryDB: Boolean): DriverParameters = copy(inMemoryDB = inMemoryDB)
|
||||
fun withCordappsForAllNodes(cordappsForAllNodes: Collection<TestCordapp>?): DriverParameters = copy(cordappsForAllNodes = cordappsForAllNodes)
|
||||
fun withEnvironmentVariables(variables : Map<String, String>): DriverParameters = copy(environmentVariables = variables)
|
||||
|
||||
fun copy(
|
||||
isDebug: Boolean,
|
||||
@ -433,4 +471,40 @@ data class DriverParameters(
|
||||
notaryCustomOverrides = emptyMap(),
|
||||
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()
|
||||
)
|
||||
}
|
@ -91,7 +91,8 @@ class DriverDSLImpl(
|
||||
val networkParameters: NetworkParameters,
|
||||
val notaryCustomOverrides: Map<String, Any?>,
|
||||
val inMemoryDB: Boolean,
|
||||
val cordappsForAllNodes: Collection<TestCordappInternal>?
|
||||
val cordappsForAllNodes: Collection<TestCordappInternal>?,
|
||||
val environmentVariables : Map<String, String>
|
||||
) : InternalDriverDSL {
|
||||
|
||||
private var _executorService: ScheduledExecutorService? = null
|
||||
@ -288,9 +289,11 @@ class DriverDSLImpl(
|
||||
} else {
|
||||
startOutOfProcessMiniNode(
|
||||
config,
|
||||
"initial-registration",
|
||||
"--network-root-truststore=${rootTruststorePath.toAbsolutePath()}",
|
||||
"--network-root-truststore-password=$rootTruststorePassword"
|
||||
arrayOf(
|
||||
"initial-registration",
|
||||
"--network-root-truststore=${rootTruststorePath.toAbsolutePath()}",
|
||||
"--network-root-truststore-password=$rootTruststorePassword"
|
||||
)
|
||||
).map { config }
|
||||
}
|
||||
}
|
||||
@ -450,7 +453,7 @@ class DriverDSLImpl(
|
||||
} else {
|
||||
// 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.
|
||||
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
|
||||
val nodeInfoFile = config.corda.baseDirectory.list { paths ->
|
||||
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
|
||||
* 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 process = startOutOfProcessNode(
|
||||
config,
|
||||
@ -545,7 +548,8 @@ class DriverDSLImpl(
|
||||
systemProperties,
|
||||
"512m",
|
||||
null,
|
||||
*extraCmdLineFlag
|
||||
environmentVariables,
|
||||
extraCmdLineFlag
|
||||
)
|
||||
|
||||
return poll(executorService, "$extraCmdLineFlag (${config.corda.myLegalName})") {
|
||||
@ -602,7 +606,15 @@ class DriverDSLImpl(
|
||||
nodeFuture
|
||||
} else {
|
||||
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
|
||||
// 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(
|
||||
config: NodeConfig,
|
||||
quasarJarPath: String,
|
||||
@ -733,9 +746,11 @@ class DriverDSLImpl(
|
||||
overriddenSystemProperties: Map<String, String>,
|
||||
maximumHeapSize: String,
|
||||
logLevelOverride: String?,
|
||||
vararg extraCmdLineFlag: String
|
||||
): Process {
|
||||
log.info("Starting out-of-process Node ${config.corda.myLegalName.organisation}, debug port is " + (debugPort ?: "not enabled"))
|
||||
environmentVariables : Map<String,String>,
|
||||
extraCmdLineFlag: Array<String> = emptyArray()
|
||||
): Process {
|
||||
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())
|
||||
|
||||
@ -774,7 +789,7 @@ class DriverDSLImpl(
|
||||
"--base-directory=${config.corda.baseDirectory}",
|
||||
"--logging-level=$loggingLevel",
|
||||
"--no-local-shell").also {
|
||||
it += extraCmdLineFlag
|
||||
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.
|
||||
@ -792,7 +807,8 @@ class DriverDSLImpl(
|
||||
extraJvmArguments = extraJvmArguments,
|
||||
workingDirectory = config.corda.baseDirectory,
|
||||
maximumHeapSize = maximumHeapSize,
|
||||
classPath = cp
|
||||
classPath = cp,
|
||||
environmentVariables = environmentVariables
|
||||
)
|
||||
}
|
||||
|
||||
@ -1013,7 +1029,8 @@ fun <DI : DriverDSL, D : InternalDriverDSL, A> genericDriver(
|
||||
networkParameters = defaultParameters.networkParameters,
|
||||
notaryCustomOverrides = defaultParameters.notaryCustomOverrides,
|
||||
inMemoryDB = defaultParameters.inMemoryDB,
|
||||
cordappsForAllNodes = uncheckedCast(defaultParameters.cordappsForAllNodes)
|
||||
cordappsForAllNodes = uncheckedCast(defaultParameters.cordappsForAllNodes),
|
||||
environmentVariables = defaultParameters.environmentVariables
|
||||
)
|
||||
)
|
||||
val shutdownHook = addShutdownHook(driverDsl::shutdown)
|
||||
@ -1090,6 +1107,7 @@ class SplitCompatibilityZoneParams(
|
||||
override fun config() : NetworkServicesConfig = config
|
||||
}
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
fun <A> internalDriver(
|
||||
isDebug: Boolean = DriverParameters().isDebug,
|
||||
driverDirectory: Path = DriverParameters().driverDirectory,
|
||||
@ -1107,6 +1125,7 @@ fun <A> internalDriver(
|
||||
notaryCustomOverrides: Map<String, Any?> = DriverParameters().notaryCustomOverrides,
|
||||
inMemoryDB: Boolean = DriverParameters().inMemoryDB,
|
||||
cordappsForAllNodes: Collection<TestCordappInternal>? = null,
|
||||
environmentVariables: Map<String, String> = emptyMap(),
|
||||
dsl: DriverDSLImpl.() -> A
|
||||
): A {
|
||||
return genericDriver(
|
||||
@ -1126,7 +1145,8 @@ fun <A> internalDriver(
|
||||
networkParameters = networkParameters,
|
||||
notaryCustomOverrides = notaryCustomOverrides,
|
||||
inMemoryDB = inMemoryDB,
|
||||
cordappsForAllNodes = cordappsForAllNodes
|
||||
cordappsForAllNodes = cordappsForAllNodes,
|
||||
environmentVariables = environmentVariables
|
||||
),
|
||||
coerce = { it },
|
||||
dsl = dsl
|
||||
|
@ -7,17 +7,29 @@ import java.time.ZonedDateTime
|
||||
import java.time.format.DateTimeFormatter
|
||||
|
||||
object ProcessUtilities {
|
||||
@Suppress("LongParameterList")
|
||||
inline fun <reified C : Any> startJavaProcess(
|
||||
arguments: List<String>,
|
||||
classPath: List<String> = defaultClassPath,
|
||||
workingDirectory: Path? = null,
|
||||
jdwpPort: Int? = null,
|
||||
extraJvmArguments: List<String> = emptyList(),
|
||||
maximumHeapSize: String? = null
|
||||
maximumHeapSize: String? = null,
|
||||
environmentVariables: Map<String, String> = emptyMap()
|
||||
): 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(
|
||||
className: String,
|
||||
arguments: List<String>,
|
||||
@ -25,7 +37,8 @@ object ProcessUtilities {
|
||||
workingDirectory: Path? = null,
|
||||
jdwpPort: Int? = null,
|
||||
extraJvmArguments: List<String> = emptyList(),
|
||||
maximumHeapSize: String? = null
|
||||
maximumHeapSize: String? = null,
|
||||
environmentVariables: Map<String,String> = emptyMap()
|
||||
): Process {
|
||||
val command = mutableListOf<String>().apply {
|
||||
add(javaPath)
|
||||
@ -38,6 +51,7 @@ object ProcessUtilities {
|
||||
}
|
||||
return ProcessBuilder(command).apply {
|
||||
inheritIO()
|
||||
environment().putAll(environmentVariables)
|
||||
environment()["CLASSPATH"] = classPath.joinToString(File.pathSeparator)
|
||||
if (workingDirectory != null) {
|
||||
// Timestamp may be handy if the same process started, killed and then re-started. Without timestamp
|
||||
|
@ -104,6 +104,7 @@ val fakeNodeLegalName = CordaX500Name(organisation = "Not:a:real:name", locality
|
||||
private val globalPortAllocation = incrementalPortAllocation()
|
||||
private val globalDebugPortAllocation = incrementalPortAllocation()
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
fun <A> rpcDriver(
|
||||
isDebug: Boolean = false,
|
||||
driverDirectory: Path = Paths.get("build") / "rpc-driver" / getTimestampAsDirectoryName(),
|
||||
@ -121,6 +122,7 @@ fun <A> rpcDriver(
|
||||
notaryCustomOverrides: Map<String, Any?> = emptyMap(),
|
||||
inMemoryDB: Boolean = true,
|
||||
cordappsForAllNodes: Collection<TestCordappInternal>? = null,
|
||||
environmentVariables: Map<String, String> = emptyMap(),
|
||||
dsl: RPCDriverDSL.() -> A
|
||||
): A {
|
||||
return genericDriver(
|
||||
@ -141,7 +143,8 @@ fun <A> rpcDriver(
|
||||
networkParameters = networkParameters,
|
||||
notaryCustomOverrides = notaryCustomOverrides,
|
||||
inMemoryDB = inMemoryDB,
|
||||
cordappsForAllNodes = cordappsForAllNodes
|
||||
cordappsForAllNodes = cordappsForAllNodes,
|
||||
environmentVariables = environmentVariables
|
||||
), externalTrace
|
||||
),
|
||||
coerce = { it },
|
||||
|
@ -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("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)
|
||||
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 = {}) {
|
||||
@ -166,7 +166,7 @@ object InteractiveShell {
|
||||
}
|
||||
|
||||
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 classpathDriver = ClassPathMountFactory(classLoader)
|
||||
val fileDriver = FileMountFactory(Utils.getCurrentDirectory())
|
||||
@ -192,7 +192,8 @@ object InteractiveShell {
|
||||
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)
|
||||
context.refresh()
|
||||
this.config = config
|
||||
|
@ -175,11 +175,13 @@ abstract class ANSIProgressRenderer {
|
||||
var indent = 0
|
||||
while (errorToPrint != null) {
|
||||
ansi.fgRed()
|
||||
ansi.a("${IntStream.range(indent, indent).mapToObj { "\t" }.toList().joinToString(separator = "") { s -> s }} $errorIcon ${error.message}")
|
||||
ansi.reset()
|
||||
ansi.a("${"\t".repeat(indent)}$errorIcon ${errorToPrint.message}")
|
||||
ansi.newline()
|
||||
errorToPrint = errorToPrint.cause
|
||||
indent++
|
||||
}
|
||||
ansi.reset()
|
||||
|
||||
ansi.eraseLine(Ansi.Erase.FORWARD)
|
||||
ansi.newline()
|
||||
newLinesDrawn++
|
||||
|
@ -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
|
||||
// given in the configuration code in InteractiveShell.
|
||||
|
||||
welcome = """
|
||||
|
||||
Welcome to the Corda interactive shell.
|
||||
Useful commands include 'help' to see what is available, and 'bye' to shut down the node.
|
||||
|
||||
"""
|
||||
welcome = { ->
|
||||
if (crash.context.attributes["crash.localShell"] == true) {
|
||||
"""
|
||||
|
||||
Welcome to the Corda interactive shell.
|
||||
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 = { ->
|
||||
return "${new Date()}>>> "
|
||||
|
Loading…
Reference in New Issue
Block a user