mirror of
https://github.com/corda/corda.git
synced 2025-06-19 07:38:22 +00:00
Merge branch 'release/os/4.12' into merge-release/os/4.11-release/os/4.12-2024-08-13-341
# Conflicts: # docker/src/docker/DockerfileAL # node/src/integration-test/kotlin/net/corda/node/services/identity/NotaryCertificateRotationTest.kt
This commit is contained in:
@ -1,14 +1,15 @@
|
||||
apply plugin: 'kotlin'
|
||||
apply plugin: 'kotlin-jpa'
|
||||
apply plugin: 'org.jetbrains.kotlin.jvm'
|
||||
apply plugin: 'org.jetbrains.kotlin.plugin.jpa'
|
||||
apply plugin: 'net.corda.plugins.quasar-utils'
|
||||
apply plugin: 'net.corda.plugins.publish-utils'
|
||||
apply plugin: 'net.corda.plugins.api-scanner'
|
||||
apply plugin: 'com.jfrog.artifactory'
|
||||
apply plugin: 'corda.common-publishing'
|
||||
|
||||
description 'Corda Node Driver module'
|
||||
|
||||
//noinspection GroovyAssignabilityCheck
|
||||
configurations {
|
||||
integrationTestCompile.extendsFrom testCompile
|
||||
integrationTestRuntime.extendsFrom testRuntime
|
||||
integrationTestImplementation.extendsFrom testImplementation
|
||||
integrationTestRuntime.extendsFrom testRuntimeOnly
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
@ -25,14 +26,33 @@ sourceSets {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile project(':test-utils')
|
||||
implementation project(':core')
|
||||
implementation project(':node')
|
||||
implementation project(':node-api')
|
||||
implementation project(':serialization')
|
||||
implementation project(':client:rpc')
|
||||
implementation project(':client:mock')
|
||||
implementation project(':common-configuration-parsing')
|
||||
implementation project(':common-validation')
|
||||
implementation project(':core-test-utils')
|
||||
implementation project(':test-common')
|
||||
implementation project(':test-utils')
|
||||
implementation project(':tools:cliutils')
|
||||
|
||||
compile group: 'org.apache.sshd', name: 'sshd-common', version: '2.9.2'
|
||||
implementation group: 'org.apache.sshd', name: 'sshd-common', version: '2.12.1'
|
||||
implementation "javax.persistence:javax.persistence-api:2.2"
|
||||
|
||||
// Integration test helpers
|
||||
testCompile "org.assertj:assertj-core:$assertj_version"
|
||||
testImplementation "org.assertj:assertj-core:$assertj_version"
|
||||
|
||||
integrationTestImplementation project(":client:jackson")
|
||||
integrationTestImplementation "junit:junit:$junit_version"
|
||||
integrationTestImplementation "org.junit.jupiter:junit-jupiter-api:${junit_jupiter_version}"
|
||||
integrationTestImplementation "info.picocli:picocli:$picocli_version"
|
||||
integrationTestImplementation "com.google.guava:guava:$guava_version"
|
||||
integrationTestImplementation 'com.googlecode.json-simple:json-simple:1.1.1'
|
||||
integrationTestImplementation "com.fasterxml.jackson.core:jackson-databind:$jackson_version"
|
||||
integrationTestImplementation 'org.hamcrest:hamcrest-library:2.1'
|
||||
|
||||
integrationTestRuntimeOnly "org.junit.vintage:junit-vintage-engine:${junit_vintage_version}"
|
||||
integrationTestRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${junit_jupiter_version}"
|
||||
@ -40,24 +60,60 @@ dependencies {
|
||||
|
||||
// Jetty dependencies for NetworkMapClient test.
|
||||
// Web stuff: for HTTP[S] servlets
|
||||
compile "org.eclipse.jetty:jetty-servlet:${jetty_version}"
|
||||
compile "org.eclipse.jetty:jetty-webapp:${jetty_version}"
|
||||
compile "javax.servlet:javax.servlet-api:${servlet_version}"
|
||||
implementation "org.eclipse.jetty.ee10:jetty-ee10-servlet:${jetty_version}"
|
||||
implementation "org.eclipse.jetty.ee10:jetty-ee10-webapp:${jetty_version}"
|
||||
implementation "javax.servlet:javax.servlet-api:${servlet_version}"
|
||||
|
||||
implementation "org.gradle:gradle-tooling-api:7.1"
|
||||
|
||||
compile "org.gradle:gradle-tooling-api:${gradle.gradleVersion}"
|
||||
|
||||
// Jersey for JAX-RS implementation for use in Jetty
|
||||
compile "org.glassfish.jersey.core:jersey-server:${jersey_version}"
|
||||
compile "org.glassfish.jersey.containers:jersey-container-servlet-core:${jersey_version}"
|
||||
compile "org.glassfish.jersey.containers:jersey-container-jetty-http:${jersey_version}"
|
||||
implementation "org.glassfish.jersey.core:jersey-server:${jersey_version}"
|
||||
implementation "org.glassfish.jersey.containers:jersey-container-servlet-core:${jersey_version}"
|
||||
implementation "org.glassfish.jersey.containers:jersey-container-jetty-http:${jersey_version}"
|
||||
implementation "org.glassfish.jersey.inject:jersey-hk2:$jersey_version"
|
||||
|
||||
implementation "io.reactivex:rxjava:$rxjava_version"
|
||||
implementation("org.apache.activemq:artemis-core-client:${artemis_version}") {
|
||||
exclude group: 'org.jgroups', module: 'jgroups'
|
||||
}
|
||||
|
||||
// Bouncy castle support needed for X509 certificate manipulation
|
||||
implementation "org.bouncycastle:bcprov-lts8on:${bouncycastle_version}"
|
||||
implementation "org.bouncycastle:bcpkix-lts8on:${bouncycastle_version}"
|
||||
implementation "org.bouncycastle:bcutil-lts8on:${bouncycastle_version}"
|
||||
|
||||
implementation "com.google.code.findbugs:jsr305:$jsr305_version"
|
||||
implementation "com.google.jimfs:jimfs:1.1"
|
||||
implementation group: "com.typesafe", name: "config", version: typesafe_config_version
|
||||
implementation "io.github.classgraph:classgraph:$class_graph_version"
|
||||
implementation "com.squareup.okhttp3:okhttp:$okhttp_version"
|
||||
implementation "org.mockito.kotlin:mockito-kotlin:$mockito_kotlin_version"
|
||||
implementation "com.esotericsoftware:kryo:$kryo_version"
|
||||
implementation "io.dropwizard.metrics:metrics-jmx:$metrics_version"
|
||||
implementation "org.apache.commons:commons-lang3:$commons_lang3_version"
|
||||
implementation "org.assertj:assertj-core:${assertj_version}"
|
||||
implementation "org.apache.logging.log4j:log4j-core:$log4j_version"
|
||||
implementation "junit:junit:$junit_version"
|
||||
|
||||
implementation("org.apache.activemq:artemis-server:${artemis_version}") {
|
||||
exclude group: 'org.apache.commons', module: 'commons-dbcp2'
|
||||
exclude group: 'org.jgroups', module: 'jgroups'
|
||||
}
|
||||
|
||||
implementation "co.paralleluniverse:quasar-core:$quasar_version"
|
||||
}
|
||||
|
||||
compileJava {
|
||||
doFirst {
|
||||
if (JavaVersion.current() == JavaVersion.VERSION_11)
|
||||
options.compilerArgs = [
|
||||
'--add-exports', 'java.base/sun.nio.ch=ALL-UNNAMED'
|
||||
]
|
||||
options.compilerArgs = [
|
||||
'--add-modules', 'jdk.incubator.foreign'
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
processResources {
|
||||
from(project(":node:capsule").files("src/main/resources/node-jvm-args.txt")) {
|
||||
into("net/corda/testing/node/internal")
|
||||
}
|
||||
}
|
||||
|
||||
@ -75,20 +131,10 @@ jar {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
tasks.named('javadocJar', Jar) {
|
||||
from 'README.md'
|
||||
include 'README.md'
|
||||
}
|
||||
|
||||
tasks.named('javadoc', Javadoc) {
|
||||
enabled = false
|
||||
}
|
||||
|
||||
publish {
|
||||
name jar.baseName
|
||||
}
|
||||
|
||||
scanApi {
|
||||
//Constructors that are synthesized by Kotlin unexpectedly
|
||||
excludeMethods = [
|
||||
@ -99,4 +145,13 @@ scanApi {
|
||||
"<init>(Lnet/corda/testing/node/InMemoryMessagingNetwork\$PeerHandle;Lnet/corda/node/services/messaging/Message;Lnet/corda/core/messaging/MessageRecipients;Lkotlin/jvm/internal/DefaultConstructorMarker;)V"
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
maven(MavenPublication) {
|
||||
artifactId jar.baseName
|
||||
from components.java
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,10 +6,6 @@ import net.corda.core.internal.CertRole
|
||||
import net.corda.core.internal.concurrent.fork
|
||||
import net.corda.core.internal.concurrent.openFuture
|
||||
import net.corda.core.internal.concurrent.transpose
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.internal.isRegularFile
|
||||
import net.corda.core.internal.list
|
||||
import net.corda.core.internal.readLines
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.node.internal.NodeStartup
|
||||
import net.corda.testing.common.internal.ProjectStructure.projectRootDir
|
||||
@ -26,12 +22,16 @@ import org.assertj.core.api.Assertions.assertThatCode
|
||||
import org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||
import org.json.simple.JSONObject
|
||||
import org.junit.Test
|
||||
import java.util.*
|
||||
import java.util.LinkedList
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.ForkJoinPool
|
||||
import java.util.concurrent.ScheduledExecutorService
|
||||
import kotlin.streams.toList
|
||||
import kotlin.io.path.div
|
||||
import kotlin.io.path.isRegularFile
|
||||
import kotlin.io.path.name
|
||||
import kotlin.io.path.useDirectoryEntries
|
||||
import kotlin.io.path.useLines
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class DriverTests {
|
||||
@ -99,8 +99,8 @@ class DriverTests {
|
||||
systemProperties = mapOf("log4j.configurationFile" to logConfigFile.toString())
|
||||
)) {
|
||||
val baseDirectory = startNode(providedName = DUMMY_BANK_A_NAME).getOrThrow().baseDirectory
|
||||
val logFile = (baseDirectory / NodeStartup.LOGS_DIRECTORY_NAME).list { it.filter { a -> a.isRegularFile() && a.fileName.toString().startsWith("node") }.findFirst().get() }
|
||||
val debugLinesPresent = logFile.readLines { lines -> lines.anyMatch { line -> line.startsWith("[DEBUG]") } }
|
||||
val logFile = (baseDirectory / NodeStartup.LOGS_DIRECTORY_NAME).useDirectoryEntries { it.single { a -> a.isRegularFile() && a.name.startsWith("node") } }
|
||||
val debugLinesPresent = logFile.useLines { lines -> lines.any { line -> line.startsWith("[DEBUG]") } }
|
||||
assertThat(debugLinesPresent).isTrue()
|
||||
}
|
||||
}
|
||||
@ -185,5 +185,5 @@ class DriverTests {
|
||||
testFuture.getOrThrow()
|
||||
}
|
||||
|
||||
private fun DriverDSL.newNode(name: CordaX500Name) = { startNode(NodeParameters(providedName = name)) }
|
||||
}
|
||||
private fun DriverDSL.newNode(name: CordaX500Name): () -> CordaFuture<NodeHandle> = { startNode(NodeParameters(providedName = name)) }
|
||||
}
|
||||
|
@ -3,9 +3,13 @@ package net.corda.testing.node
|
||||
import co.paralleluniverse.fibers.Suspendable
|
||||
import net.corda.client.jackson.JacksonSupport
|
||||
import net.corda.client.rpc.CordaRPCClient
|
||||
import net.corda.core.flows.*
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.internal.list
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.flows.FlowSession
|
||||
import net.corda.core.flows.FlowStackSnapshot
|
||||
import net.corda.core.flows.InitiatedBy
|
||||
import net.corda.core.flows.InitiatingFlow
|
||||
import net.corda.core.flows.StartableByRPC
|
||||
import net.corda.core.flows.StateMachineRunId
|
||||
import net.corda.core.internal.read
|
||||
import net.corda.core.messaging.startFlow
|
||||
import net.corda.core.serialization.CordaSerializable
|
||||
@ -16,6 +20,8 @@ import org.junit.Ignore
|
||||
import org.junit.Test
|
||||
import java.nio.file.Path
|
||||
import java.time.LocalDate
|
||||
import kotlin.io.path.div
|
||||
import kotlin.io.path.useDirectoryEntries
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNull
|
||||
import kotlin.test.assertTrue
|
||||
@ -29,11 +35,11 @@ data class StackSnapshotFrame(val method: String, val clazz: String, val dataTyp
|
||||
* an empty list the frame is considered to be full.
|
||||
*/
|
||||
fun convertToStackSnapshotFrames(snapshot: FlowStackSnapshot): List<StackSnapshotFrame> {
|
||||
return snapshot.stackFrames.map {
|
||||
val dataTypes = it.stackObjects.map {
|
||||
return snapshot.stackFrames.map { frame ->
|
||||
val dataTypes = frame.stackObjects.map {
|
||||
if (it == null) null else it::class.qualifiedName
|
||||
}
|
||||
val stackTraceElement = it.stackTraceElement
|
||||
val stackTraceElement = frame.stackTraceElement
|
||||
StackSnapshotFrame(stackTraceElement.methodName, stackTraceElement.className, dataTypes)
|
||||
}
|
||||
}
|
||||
@ -48,7 +54,7 @@ fun convertToStackSnapshotFrames(snapshot: FlowStackSnapshot): List<StackSnapsho
|
||||
*/
|
||||
@StartableByRPC
|
||||
class SideEffectFlow : FlowLogic<List<StackSnapshotFrame>>() {
|
||||
var sideEffectField = ""
|
||||
private var sideEffectField = ""
|
||||
|
||||
@Suspendable
|
||||
override fun call(): List<StackSnapshotFrame> {
|
||||
@ -155,7 +161,7 @@ class PersistingSideEffectFlow : FlowLogic<StateMachineRunId>() {
|
||||
* Similar to [PersistingSideEffectFlow] but aims to produce multiple snapshot files.
|
||||
*/
|
||||
@StartableByRPC
|
||||
class MultiplePersistingSideEffectFlow(val persistCallCount: Int) : FlowLogic<StateMachineRunId>() {
|
||||
class MultiplePersistingSideEffectFlow(private val persistCallCount: Int) : FlowLogic<StateMachineRunId>() {
|
||||
|
||||
@Suspendable
|
||||
override fun call(): StateMachineRunId {
|
||||
@ -212,7 +218,7 @@ private fun flowSnapshotDir(baseDir: Path, flowId: StateMachineRunId): Path {
|
||||
}
|
||||
|
||||
fun countFilesInDir(baseDir: Path, flowId: StateMachineRunId): Int {
|
||||
return flowSnapshotDir(baseDir, flowId).list { it.count().toInt() }
|
||||
return flowSnapshotDir(baseDir, flowId).useDirectoryEntries { it.count() }
|
||||
}
|
||||
|
||||
fun assertFrame(expectedMethod: String, expectedEmpty: Boolean, frame: StackSnapshotFrame) {
|
||||
@ -311,9 +317,9 @@ class FlowStackSnapshotTest {
|
||||
val snapshotFromFile = readFlowStackSnapshotFromDir(a.baseDirectory, flowId)
|
||||
var inCallCount = 0
|
||||
var inPersistCount = 0
|
||||
snapshotFromFile.stackFrames.forEach {
|
||||
val trace = it.stackTraceElement
|
||||
it.stackObjects.forEach {
|
||||
snapshotFromFile.stackFrames.forEach { frame ->
|
||||
val trace = frame.stackTraceElement
|
||||
frame.stackObjects.forEach {
|
||||
when (it) {
|
||||
Constants.IN_CALL_VALUE -> {
|
||||
assertEquals(PersistingSideEffectFlow::call.name, trace.methodName)
|
||||
|
@ -1,10 +1,14 @@
|
||||
package net.corda.testing.node
|
||||
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.internal.deleteRecursively
|
||||
import net.corda.testing.common.internal.ProjectStructure.projectRootDir
|
||||
import net.corda.testing.node.internal.ProcessUtilities.startJavaProcess
|
||||
import net.corda.testing.node.internal.nodeJvmArgs
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.io.path.Path
|
||||
import kotlin.io.path.createDirectories
|
||||
import kotlin.io.path.div
|
||||
|
||||
class MockNetworkIntegrationTests {
|
||||
companion object {
|
||||
@ -21,6 +25,17 @@ class MockNetworkIntegrationTests {
|
||||
@Test(timeout=300_000)
|
||||
fun `does not leak non-daemon threads`() {
|
||||
val quasar = projectRootDir / "lib" / "quasar.jar"
|
||||
assertEquals(0, startJavaProcess<MockNetworkIntegrationTests>(emptyList(), extraJvmArguments = listOf("-javaagent:$quasar")).waitFor())
|
||||
val quasarOptions = "m"
|
||||
|
||||
val workingDirectory = Path("build", "MockNetworkIntegrationTests").apply {
|
||||
deleteRecursively()
|
||||
createDirectories()
|
||||
}
|
||||
val process = startJavaProcess<MockNetworkIntegrationTests>(
|
||||
emptyList(),
|
||||
workingDirectory = workingDirectory,
|
||||
extraJvmArguments = listOf("-javaagent:$quasar=$quasarOptions") + nodeJvmArgs
|
||||
)
|
||||
assertThat(process.waitFor()).isZero()
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,7 @@
|
||||
package net.corda.testing.node.internal
|
||||
|
||||
import net.corda.testing.internal.IS_OPENJ9
|
||||
import org.hamcrest.MatcherAssert.assertThat
|
||||
import org.hamcrest.Matchers.matchesPattern
|
||||
import org.junit.Assume
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.Parameterized
|
||||
@ -11,7 +9,6 @@ import java.io.BufferedReader
|
||||
import java.io.InputStreamReader
|
||||
import java.util.stream.Collectors
|
||||
|
||||
|
||||
@RunWith(value = Parameterized::class)
|
||||
class CordaCliWrapperErrorHandlingTests(val arguments: List<String>, val outputRegexPattern: String) {
|
||||
|
||||
@ -33,22 +30,19 @@ class CordaCliWrapperErrorHandlingTests(val arguments: List<String>, val outputR
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `Run CordaCliWrapper sample app with arguments and check error output matches regExp`() {
|
||||
// For openj9 the process error output appears sometimes to be garbled.
|
||||
Assume.assumeTrue(!IS_OPENJ9)
|
||||
val process = ProcessUtilities.startJavaProcess(
|
||||
className = className,
|
||||
arguments = arguments,
|
||||
inheritIO = false)
|
||||
val process = ProcessUtilities.startJavaProcess(className = className, arguments = arguments)
|
||||
|
||||
process.waitFor()
|
||||
|
||||
val processErrorOutput = BufferedReader(
|
||||
InputStreamReader(process.errorStream))
|
||||
.lines()
|
||||
.filter { !it.startsWith("Warning: Nashorn") }
|
||||
.filter { it.contains("Exception") ||
|
||||
it.contains("at ") ||
|
||||
it.contains("exception") }
|
||||
.collect(Collectors.joining("\n"))
|
||||
.toString()
|
||||
|
||||
assertThat(processErrorOutput, matchesPattern(outputRegexPattern))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,13 @@
|
||||
package net.corda.testing.node.internal
|
||||
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.internal.deleteRecursively
|
||||
import net.corda.testing.common.internal.ProjectStructure.projectRootDir
|
||||
import net.corda.testing.node.internal.ProcessUtilities.startJavaProcess
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.io.path.Path
|
||||
import kotlin.io.path.createDirectories
|
||||
import kotlin.io.path.div
|
||||
|
||||
class InternalMockNetworkIntegrationTests {
|
||||
companion object {
|
||||
@ -21,6 +24,17 @@ class InternalMockNetworkIntegrationTests {
|
||||
@Test(timeout=300_000)
|
||||
fun `does not leak non-daemon threads`() {
|
||||
val quasar = projectRootDir / "lib" / "quasar.jar"
|
||||
assertEquals(0, startJavaProcess<InternalMockNetworkIntegrationTests>(emptyList(), extraJvmArguments = listOf("-javaagent:$quasar")).waitFor())
|
||||
val quasarOptions = "m"
|
||||
|
||||
val workingDirectory = Path("build", "InternalMockNetworkIntegrationTests").apply {
|
||||
deleteRecursively()
|
||||
createDirectories()
|
||||
}
|
||||
val process = startJavaProcess<InternalMockNetworkIntegrationTests>(
|
||||
emptyList(),
|
||||
workingDirectory = workingDirectory,
|
||||
extraJvmArguments = listOf("-javaagent:$quasar=$quasarOptions") + nodeJvmArgs
|
||||
)
|
||||
assertThat(process.waitFor()).isZero()
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
package net.corda.testing.node.internal
|
||||
|
||||
import net.corda.core.internal.readText
|
||||
import net.corda.core.internal.writeText
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TemporaryFolder
|
||||
import java.nio.file.Paths
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.io.path.readText
|
||||
import kotlin.io.path.writeText
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
|
@ -1,97 +1,108 @@
|
||||
package net.corda.testing.driver;
|
||||
|
||||
import sun.misc.Unsafe;
|
||||
import sun.nio.ch.DirectBuffer;
|
||||
import jdk.incubator.foreign.MemoryHandles;
|
||||
import jdk.incubator.foreign.MemorySegment;
|
||||
import jdk.incubator.foreign.ResourceScope;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.lang.reflect.Field;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.net.ServerSocket;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.MappedByteBuffer;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.channels.FileChannel.MapMode;
|
||||
import java.nio.file.FileAlreadyExistsException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
/**
|
||||
* JDK11 upgrade: rewritten in Java to gain access to private internal JDK classes via module directives (not available to Kotlin compiler):
|
||||
* import sun.misc.Unsafe;
|
||||
* import sun.nio.ch.DirectBuffer;
|
||||
*/
|
||||
import static java.nio.file.StandardOpenOption.READ;
|
||||
import static java.nio.file.StandardOpenOption.WRITE;
|
||||
|
||||
// This was originally (re)written in Java to access internal JDK APIs. Since it's no longer doing that, this can be converted back to Kotlin.
|
||||
public class SharedMemoryIncremental extends PortAllocation {
|
||||
private static final int DEFAULT_START_PORT = 10_000;
|
||||
private static final int FIRST_EPHEMERAL_PORT = 30_000;
|
||||
|
||||
static private final int DEFAULT_START_PORT = 10_000;
|
||||
static private final int FIRST_EPHEMERAL_PORT = 30_000;
|
||||
private final int startPort;
|
||||
private final int endPort;
|
||||
|
||||
private int startPort;
|
||||
private int endPort;
|
||||
|
||||
private MappedByteBuffer mb;
|
||||
private Long startingAddress;
|
||||
|
||||
private File file = new File(System.getProperty("user.home"), "corda-" + startPort + "-to-" + endPort + "-port-allocator.bin");
|
||||
private RandomAccessFile backingFile;
|
||||
{
|
||||
try {
|
||||
backingFile = new RandomAccessFile(file, "rw");
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
private final MemorySegment memorySegment;
|
||||
private final VarHandle intHandle;
|
||||
private final MappedByteBuffer unsafeBuffer;
|
||||
|
||||
private SharedMemoryIncremental(int startPort, int endPort) {
|
||||
this.startPort = startPort;
|
||||
this.endPort = endPort;
|
||||
Path file = Path.of(System.getProperty("user.home"), "corda-" + startPort + "-to-" + endPort + "-port-allocator.bin");
|
||||
try {
|
||||
mb = backingFile.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, 16);
|
||||
startingAddress = ((DirectBuffer) mb).address();
|
||||
try {
|
||||
Files.createFile(file);
|
||||
} catch (FileAlreadyExistsException ignored) {}
|
||||
if (isFfmAvailable()) {
|
||||
memorySegment = MemorySegment.mapFile(file, 0, Integer.SIZE, MapMode.READ_WRITE, ResourceScope.globalScope());
|
||||
intHandle = MemoryHandles.varHandle(int.class, ByteOrder.nativeOrder());
|
||||
unsafeBuffer = null;
|
||||
} else {
|
||||
LoggerFactory.getLogger(getClass()).warn("Using unsafe port allocator which may lead to the same port being allocated " +
|
||||
"twice. Consider adding --add-modules=jdk.incubator.foreign to the test JVM.");
|
||||
memorySegment = null;
|
||||
intHandle = null;
|
||||
unsafeBuffer = FileChannel.open(file, READ, WRITE).map(MapMode.READ_WRITE, 0, Integer.SIZE);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isFfmAvailable() {
|
||||
try {
|
||||
Class.forName("jdk.incubator.foreign.MemorySegment");
|
||||
return true;
|
||||
} catch (ClassNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static SharedMemoryIncremental INSTANCE = new SharedMemoryIncremental(DEFAULT_START_PORT, FIRST_EPHEMERAL_PORT);
|
||||
static private Unsafe UNSAFE = getUnsafe();
|
||||
|
||||
static private Unsafe getUnsafe() {
|
||||
try {
|
||||
Field f = Unsafe.class.getDeclaredField("theUnsafe");
|
||||
f.setAccessible(true);
|
||||
return (Unsafe) f.get(null);
|
||||
} catch (NoSuchFieldException | IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int nextPort() {
|
||||
long oldValue;
|
||||
long newValue;
|
||||
boolean loopSuccess;
|
||||
do {
|
||||
oldValue = UNSAFE.getLongVolatile(null, startingAddress);
|
||||
while (true) {
|
||||
int oldValue;
|
||||
if (intHandle != null) {
|
||||
oldValue = (int) intHandle.getVolatile(memorySegment, 0L);
|
||||
} else {
|
||||
oldValue = unsafeBuffer.getInt(0);
|
||||
}
|
||||
int newValue;
|
||||
if (oldValue + 1 >= endPort || oldValue < startPort) {
|
||||
newValue = startPort;
|
||||
} else {
|
||||
newValue = (oldValue + 1);
|
||||
}
|
||||
boolean reserveSuccess = UNSAFE.compareAndSwapLong(null, startingAddress, oldValue, newValue);
|
||||
loopSuccess = reserveSuccess && isLocalPortAvailable(newValue);
|
||||
} while (!loopSuccess);
|
||||
|
||||
return (int) newValue;
|
||||
if (intHandle != null) {
|
||||
if (!intHandle.compareAndSet(memorySegment, 0L, oldValue, newValue)) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
unsafeBuffer.putInt(0, newValue);
|
||||
}
|
||||
if (isLocalPortAvailable(newValue)) {
|
||||
return newValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private boolean isLocalPortAvailable(Long portToTest) {
|
||||
try (ServerSocket serverSocket = new ServerSocket(Math.toIntExact(portToTest))) {
|
||||
private boolean isLocalPortAvailable(int portToTest) {
|
||||
try (ServerSocket ignored = new ServerSocket(portToTest)) {
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
// Don't catch anything other than IOException here in case we
|
||||
// accidentally create an infinite loop. For example, installing
|
||||
// a SecurityManager could throw AccessControlException.
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -6,7 +6,6 @@ import net.corda.core.DoNotImplement
|
||||
import net.corda.core.concurrent.CordaFuture
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.internal.uncheckedCast
|
||||
import net.corda.core.messaging.CordaRPCOps
|
||||
import net.corda.core.node.NetworkParameters
|
||||
@ -31,6 +30,7 @@ import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import java.time.Duration
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import kotlin.io.path.div
|
||||
|
||||
/**
|
||||
* Object ecapsulating a notary started automatically by the driver.
|
||||
@ -98,7 +98,6 @@ interface InProcess : NodeHandle {
|
||||
|
||||
/**
|
||||
* Starts an already constructed flow. Note that you must be on the server thread to call this method.
|
||||
* @param context indicates who started the flow, see: [InvocationContext].
|
||||
*/
|
||||
fun <T> startFlow(logic: FlowLogic<T>): CordaFuture<T> = internalServices.startFlow(logic, internalServices.newContext())
|
||||
.getOrThrow().resultFuture
|
||||
@ -628,7 +627,7 @@ data class DriverParameters(
|
||||
waitForAllNodesToFinish: Boolean,
|
||||
notarySpecs: List<NotarySpec>,
|
||||
extraCordappPackagesToScan: List<String>,
|
||||
@Suppress("DEPRECATION") jmxPolicy: JmxPolicy,
|
||||
jmxPolicy: JmxPolicy,
|
||||
networkParameters: NetworkParameters,
|
||||
notaryCustomOverrides: Map<String, Any?>,
|
||||
inMemoryDB: Boolean,
|
||||
|
@ -25,7 +25,7 @@ import net.corda.testing.node.User
|
||||
* log level argument.
|
||||
* @property rpcAddress optional override for RPC address on which node will be accepting RPC connections from the clients. Port provided must be vacant.
|
||||
*/
|
||||
@Suppress("unused")
|
||||
@Suppress("unused", "TooManyFunctions")
|
||||
data class NodeParameters(
|
||||
val providedName: CordaX500Name? = null,
|
||||
val rpcUsers: List<User> = emptyList(),
|
||||
@ -37,7 +37,8 @@ data class NodeParameters(
|
||||
val flowOverrides: Map<out Class<out FlowLogic<*>>, Class<out FlowLogic<*>>> = emptyMap(),
|
||||
val logLevelOverride: String? = null,
|
||||
val rpcAddress: NetworkHostAndPort? = null,
|
||||
val systemProperties: Map<String, String> = emptyMap()
|
||||
val systemProperties: Map<String, String> = emptyMap(),
|
||||
val legacyContracts: Collection<TestCordapp> = emptySet()
|
||||
) {
|
||||
/**
|
||||
* Create a new node parameters object with default values. Each parameter can be specified with its wither method which returns a copy
|
||||
@ -54,6 +55,9 @@ data class NodeParameters(
|
||||
fun withAdditionalCordapps(additionalCordapps: Set<TestCordapp>): NodeParameters = copy(additionalCordapps = additionalCordapps)
|
||||
fun withFlowOverrides(flowOverrides: Map<Class<out FlowLogic<*>>, Class<out FlowLogic<*>>>): NodeParameters = copy(flowOverrides = flowOverrides)
|
||||
fun withLogLevelOverride(logLevelOverride: String?): NodeParameters = copy(logLevelOverride = logLevelOverride)
|
||||
fun withRpcAddress(rpcAddress: NetworkHostAndPort?): NodeParameters = copy(rpcAddress = rpcAddress)
|
||||
fun withSystemProperties(systemProperties: Map<String, String>): NodeParameters = copy(systemProperties = systemProperties)
|
||||
fun withLegacyContracts(legacyContracts: Collection<TestCordapp>): NodeParameters = copy(legacyContracts = legacyContracts)
|
||||
|
||||
constructor(
|
||||
providedName: CordaX500Name?,
|
||||
@ -221,4 +225,58 @@ data class NodeParameters(
|
||||
logLevelOverride = logLevelOverride,
|
||||
rpcAddress = rpcAddress,
|
||||
systemProperties = systemProperties)
|
||||
|
||||
constructor(
|
||||
providedName: CordaX500Name?,
|
||||
rpcUsers: List<User>,
|
||||
verifierType: VerifierType,
|
||||
customOverrides: Map<String, Any?>,
|
||||
startInSameProcess: Boolean?,
|
||||
maximumHeapSize: String,
|
||||
additionalCordapps: Collection<TestCordapp> = emptySet(),
|
||||
flowOverrides: Map<out Class<out FlowLogic<*>>, Class<out FlowLogic<*>>>,
|
||||
logLevelOverride: String? = null,
|
||||
rpcAddress: NetworkHostAndPort? = null,
|
||||
systemProperties: Map<String, String> = emptyMap()
|
||||
) : this(
|
||||
providedName,
|
||||
rpcUsers,
|
||||
verifierType,
|
||||
customOverrides,
|
||||
startInSameProcess,
|
||||
maximumHeapSize,
|
||||
additionalCordapps,
|
||||
flowOverrides,
|
||||
logLevelOverride,
|
||||
rpcAddress,
|
||||
systemProperties,
|
||||
legacyContracts = emptySet())
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
fun copy(
|
||||
providedName: CordaX500Name?,
|
||||
rpcUsers: List<User>,
|
||||
verifierType: VerifierType,
|
||||
customOverrides: Map<String, Any?>,
|
||||
startInSameProcess: Boolean?,
|
||||
maximumHeapSize: String,
|
||||
additionalCordapps: Collection<TestCordapp> = emptySet(),
|
||||
flowOverrides: Map<out Class<out FlowLogic<*>>, Class<out FlowLogic<*>>>,
|
||||
logLevelOverride: String? = null,
|
||||
rpcAddress: NetworkHostAndPort? = null,
|
||||
systemProperties: Map<String, String> = emptyMap()
|
||||
) = this.copy(
|
||||
providedName = providedName,
|
||||
rpcUsers = rpcUsers,
|
||||
verifierType = verifierType,
|
||||
customOverrides = customOverrides,
|
||||
startInSameProcess = startInSameProcess,
|
||||
maximumHeapSize = maximumHeapSize,
|
||||
additionalCordapps = additionalCordapps,
|
||||
flowOverrides = flowOverrides,
|
||||
logLevelOverride = logLevelOverride,
|
||||
rpcAddress = rpcAddress,
|
||||
systemProperties = systemProperties,
|
||||
legacyContracts = legacyContracts)
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package net.corda.testing.driver.internal
|
||||
|
||||
import jakarta.validation.constraints.NotNull
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.messaging.CordaRPCOps
|
||||
import net.corda.core.node.NodeInfo
|
||||
@ -14,7 +15,6 @@ import net.corda.testing.driver.OutOfProcess
|
||||
import net.corda.testing.node.User
|
||||
import rx.Observable
|
||||
import java.nio.file.Path
|
||||
import javax.validation.constraints.NotNull
|
||||
|
||||
interface NodeHandleInternal : NodeHandle {
|
||||
val configuration: NodeConfiguration
|
||||
|
@ -1,21 +1,31 @@
|
||||
package net.corda.testing.node
|
||||
|
||||
import com.google.common.collect.MutableClassToInstanceMap
|
||||
import net.corda.core.CordaInternal
|
||||
import net.corda.core.contracts.Attachment
|
||||
import net.corda.core.contracts.ContractClassName
|
||||
import net.corda.core.contracts.ContractState
|
||||
import net.corda.core.contracts.StateAndRef
|
||||
import net.corda.core.contracts.StateRef
|
||||
import net.corda.core.contracts.TransactionState
|
||||
import net.corda.core.cordapp.CordappProvider
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.sha256
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.flows.StateMachineRunId
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.identity.PartyAndCertificate
|
||||
import net.corda.core.internal.AbstractAttachment
|
||||
import net.corda.core.internal.PLATFORM_VERSION
|
||||
import net.corda.core.internal.VisibleForTesting
|
||||
import net.corda.core.internal.cordapp.CordappProviderInternal
|
||||
import net.corda.core.internal.getRequiredTransaction
|
||||
import net.corda.core.internal.mapToSet
|
||||
import net.corda.core.internal.requireSupportedHashType
|
||||
import net.corda.core.internal.telemetry.TelemetryComponent
|
||||
import net.corda.core.internal.telemetry.TelemetryServiceImpl
|
||||
import net.corda.core.internal.verification.ExternalVerifierHandle
|
||||
import net.corda.core.internal.verification.VerifyingServiceHub
|
||||
import net.corda.core.messaging.DataFeed
|
||||
import net.corda.core.messaging.FlowHandle
|
||||
import net.corda.core.messaging.FlowProgressHandle
|
||||
@ -34,33 +44,34 @@ import net.corda.core.node.services.NetworkMapCache
|
||||
import net.corda.core.node.services.NetworkParametersService
|
||||
import net.corda.core.node.services.ServiceLifecycleObserver
|
||||
import net.corda.core.node.services.TransactionStorage
|
||||
import net.corda.core.node.services.TransactionVerifierService
|
||||
import net.corda.core.node.services.VaultService
|
||||
import net.corda.core.node.services.diagnostics.DiagnosticsService
|
||||
import net.corda.core.node.services.vault.CordaTransactionSupport
|
||||
import net.corda.core.serialization.SerializeAsToken
|
||||
import net.corda.core.serialization.internal.AttachmentsClassLoaderCacheImpl
|
||||
import net.corda.core.transactions.SignedTransaction
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import net.corda.coretesting.internal.DEV_ROOT_CA
|
||||
import net.corda.node.VersionInfo
|
||||
import net.corda.node.internal.ServicesForResolutionImpl
|
||||
import net.corda.node.internal.NodeServicesForResolution
|
||||
import net.corda.node.internal.cordapp.JarScanningCordappLoader
|
||||
import net.corda.node.services.api.SchemaService
|
||||
import net.corda.node.services.api.ServiceHubInternal
|
||||
import net.corda.node.services.api.StateMachineRecordedTransactionMappingStorage
|
||||
import net.corda.node.services.api.VaultServiceInternal
|
||||
import net.corda.node.services.api.WritableTransactionStorage
|
||||
import net.corda.node.services.attachments.NodeAttachmentTrustCalculator
|
||||
import net.corda.node.services.diagnostics.NodeDiagnosticsService
|
||||
import net.corda.node.services.identity.InMemoryIdentityService
|
||||
import net.corda.node.services.identity.PersistentIdentityService
|
||||
import net.corda.node.services.keys.BasicHSMKeyManagementService
|
||||
import net.corda.node.services.network.PersistentNetworkMapCache
|
||||
import net.corda.node.services.persistence.PublicKeyToOwningIdentityCacheImpl
|
||||
import net.corda.node.services.persistence.toInternal
|
||||
import net.corda.node.services.schema.NodeSchemaService
|
||||
import net.corda.node.services.transactions.InMemoryTransactionVerifierService
|
||||
import net.corda.node.services.vault.NodeVaultService
|
||||
import net.corda.nodeapi.internal.cordapp.CordappLoader
|
||||
import net.corda.nodeapi.internal.cordapp.cordappSchemas
|
||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||
import net.corda.nodeapi.internal.persistence.contextTransaction
|
||||
@ -69,7 +80,6 @@ import net.corda.testing.core.TestIdentity
|
||||
import net.corda.testing.internal.MockCordappProvider
|
||||
import net.corda.testing.internal.TestingNamedCacheFactory
|
||||
import net.corda.testing.internal.configureDatabase
|
||||
import net.corda.testing.node.internal.DriverDSLImpl
|
||||
import net.corda.testing.node.internal.MockCryptoService
|
||||
import net.corda.testing.node.internal.MockKeyManagementService
|
||||
import net.corda.testing.node.internal.MockNetworkParametersStorage
|
||||
@ -78,6 +88,7 @@ import net.corda.testing.node.internal.cordappsForPackages
|
||||
import net.corda.testing.node.internal.getCallerPackage
|
||||
import net.corda.testing.services.MockAttachmentStorage
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.nio.file.FileAlreadyExistsException
|
||||
import java.nio.file.Paths
|
||||
import java.security.KeyPair
|
||||
import java.sql.Connection
|
||||
@ -116,10 +127,9 @@ open class MockServices private constructor(
|
||||
*arrayOf(initialIdentity.keyPair) + moreKeys
|
||||
)
|
||||
) : ServiceHub {
|
||||
|
||||
companion object {
|
||||
private fun cordappLoaderForPackages(packages: Iterable<String>, versionInfo: VersionInfo = VersionInfo.UNKNOWN): CordappLoader {
|
||||
return JarScanningCordappLoader.fromJarUrls(cordappsForPackages(packages).map { it.jarFile.toUri().toURL() }, versionInfo)
|
||||
return JarScanningCordappLoader(cordappsForPackages(packages).mapToSet { it.jarFile }, versionInfo = versionInfo)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -135,8 +145,8 @@ open class MockServices private constructor(
|
||||
val dbPath = dbDir.resolve("persistence")
|
||||
try {
|
||||
DatabaseSnapshot.copyDatabaseSnapshot(dbDir)
|
||||
} catch (ex: java.nio.file.FileAlreadyExistsException) {
|
||||
DriverDSLImpl.log.warn("Database already exists on disk, not attempting to pre-migrate database.")
|
||||
} catch (e: FileAlreadyExistsException) {
|
||||
loggerFor<MockServices>().warn("Database already exists on disk, not attempting to pre-migrate database.")
|
||||
}
|
||||
val props = Properties()
|
||||
props.setProperty("dataSourceClassName", "org.h2.jdbcx.JdbcDataSource")
|
||||
@ -295,22 +305,19 @@ open class MockServices private constructor(
|
||||
// Because Kotlin is dumb and makes not publicly visible objects public, thus changing the public API.
|
||||
private val mockStateMachineRecordedTransactionMappingStorage = MockStateMachineRecordedTransactionMappingStorage()
|
||||
|
||||
private val dummyAttachment by lazy {
|
||||
val inputStream = ByteArrayOutputStream().apply {
|
||||
ZipOutputStream(this).use {
|
||||
with(it) {
|
||||
putNextEntry(ZipEntry(JarFile.MANIFEST_NAME))
|
||||
}
|
||||
}
|
||||
}.toByteArray().inputStream()
|
||||
val attachment = object : Attachment {
|
||||
override val id get() = throw UnsupportedOperationException()
|
||||
override fun open() = inputStream
|
||||
override val signerKeys get() = throw UnsupportedOperationException()
|
||||
override val signers: List<Party> get() = throw UnsupportedOperationException()
|
||||
override val size: Int = 512
|
||||
private val dummyAttachment: Attachment by lazy {
|
||||
object : AbstractAttachment(
|
||||
{
|
||||
val baos = ByteArrayOutputStream()
|
||||
ZipOutputStream(baos).use { zip ->
|
||||
zip.putNextEntry(ZipEntry(JarFile.MANIFEST_NAME))
|
||||
}
|
||||
baos.toByteArray()
|
||||
},
|
||||
null
|
||||
) {
|
||||
override val id: SecureHash by lazy(attachmentData::sha256)
|
||||
}
|
||||
attachment
|
||||
}
|
||||
}
|
||||
|
||||
@ -482,27 +489,23 @@ open class MockServices private constructor(
|
||||
get() {
|
||||
return NodeInfo(listOf(NetworkHostAndPort("mock.node.services", 10000)), listOf(initialIdentity.identity), 1, serial = 1L)
|
||||
}
|
||||
private val mockCordappProvider: MockCordappProvider = MockCordappProvider(cordappLoader, attachments).also {
|
||||
private val mockCordappProvider: MockCordappProvider = MockCordappProvider(cordappLoader, attachments.toInternal()).also {
|
||||
it.start()
|
||||
}
|
||||
override val transactionVerifierService: TransactionVerifierService
|
||||
get() = InMemoryTransactionVerifierService(
|
||||
numberOfWorkers = 2,
|
||||
cordappProvider = mockCordappProvider,
|
||||
attachments = attachments
|
||||
)
|
||||
override val cordappProvider: CordappProvider get() = mockCordappProvider
|
||||
override var networkParametersService: NetworkParametersService = MockNetworkParametersStorage(initialNetworkParameters)
|
||||
override val diagnosticsService: DiagnosticsService = NodeDiagnosticsService()
|
||||
|
||||
protected val servicesForResolution: ServicesForResolution
|
||||
get() = ServicesForResolutionImpl(identityService, attachments, cordappProvider, networkParametersService, validatedTransactions)
|
||||
// This is kept here for backwards compatibility, otherwise this has no extra utility.
|
||||
protected val servicesForResolution: ServicesForResolution get() = verifyingView
|
||||
|
||||
private val verifyingView: VerifyingServiceHub by lazy { VerifyingView(this) }
|
||||
|
||||
internal fun makeVaultService(schemaService: SchemaService, database: CordaPersistence, cordappLoader: CordappLoader): VaultServiceInternal {
|
||||
return NodeVaultService(
|
||||
clock,
|
||||
keyManagementService,
|
||||
servicesForResolution as NodeServicesForResolution,
|
||||
verifyingView,
|
||||
database,
|
||||
schemaService,
|
||||
cordappLoader.appClassLoader
|
||||
@ -511,9 +514,9 @@ open class MockServices private constructor(
|
||||
|
||||
// This needs to be internal as MutableClassToInstanceMap is a guava type and shouldn't be part of our public API
|
||||
/** A map of available [CordaService] implementations */
|
||||
internal val cordappServices: MutableClassToInstanceMap<SerializeAsToken> = MutableClassToInstanceMap.create<SerializeAsToken>()
|
||||
internal val cordappServices: MutableClassToInstanceMap<SerializeAsToken> = MutableClassToInstanceMap.create()
|
||||
|
||||
internal val cordappTelemetryComponents: MutableClassToInstanceMap<TelemetryComponent> = MutableClassToInstanceMap.create<TelemetryComponent>()
|
||||
private val cordappTelemetryComponents: MutableClassToInstanceMap<TelemetryComponent> = MutableClassToInstanceMap.create()
|
||||
|
||||
override fun <T : SerializeAsToken> cordaService(type: Class<T>): T {
|
||||
require(type.isAnnotationPresent(CordaService::class.java)) { "${type.name} is not a Corda service" }
|
||||
@ -543,19 +546,46 @@ open class MockServices private constructor(
|
||||
mockCordappProvider.addMockCordapp(contractClassName, attachments)
|
||||
}
|
||||
|
||||
override fun loadState(stateRef: StateRef) = servicesForResolution.loadState(stateRef)
|
||||
override fun loadStates(stateRefs: Set<StateRef>) = servicesForResolution.loadStates(stateRefs)
|
||||
override fun loadState(stateRef: StateRef): TransactionState<ContractState> {
|
||||
return getRequiredTransaction(stateRef.txhash).resolveBaseTransaction(this).outputs[stateRef.index]
|
||||
}
|
||||
|
||||
override fun loadStates(stateRefs: Set<StateRef>): Set<StateAndRef<ContractState>> = stateRefs.mapToSet(::toStateAndRef)
|
||||
|
||||
/** Returns a dummy Attachment, in context of signature constrains non-downgrade rule this default to contract class version `1`. */
|
||||
override fun loadContractAttachment(stateRef: StateRef) = dummyAttachment
|
||||
}
|
||||
|
||||
/**
|
||||
* Function which can be used to create a mock [CordaService] for use within testing, such as an Oracle.
|
||||
*/
|
||||
fun <T : SerializeAsToken> createMockCordaService(serviceHub: MockServices, serviceConstructor: (AppServiceHub) -> T): T {
|
||||
class MockAppServiceHubImpl<out T : SerializeAsToken>(val serviceHub: MockServices, serviceConstructor: (AppServiceHub) -> T) : AppServiceHub, ServiceHub by serviceHub {
|
||||
val serviceInstance: T = serviceConstructor(this)
|
||||
|
||||
/**
|
||||
* All [ServiceHub]s must also implement [VerifyingServiceHub]. However, since [MockServices] is part of the public API, making it
|
||||
* extend [VerifyingServiceHub] would leak internal APIs. Instead we have this private view class and have the `toVerifyingServiceHub`
|
||||
* extension method return it.
|
||||
*/
|
||||
private class VerifyingView(private val mockServices: MockServices) : VerifyingServiceHub, ServiceHub by mockServices {
|
||||
override val attachmentTrustCalculator = NodeAttachmentTrustCalculator(
|
||||
attachmentStorage = mockServices.attachments.toInternal(),
|
||||
cacheFactory = TestingNamedCacheFactory()
|
||||
)
|
||||
|
||||
override val attachmentsClassLoaderCache = AttachmentsClassLoaderCacheImpl(TestingNamedCacheFactory())
|
||||
|
||||
override val cordappProvider: CordappProviderInternal get() = mockServices.mockCordappProvider
|
||||
|
||||
override fun loadContractAttachment(stateRef: StateRef): Attachment = mockServices.loadContractAttachment(stateRef)
|
||||
|
||||
override fun loadState(stateRef: StateRef): TransactionState<*> = mockServices.loadState(stateRef)
|
||||
|
||||
override fun loadStates(stateRefs: Set<StateRef>): Set<StateAndRef<ContractState>> = mockServices.loadStates(stateRefs)
|
||||
|
||||
override val externalVerifierHandle: ExternalVerifierHandle
|
||||
get() = throw UnsupportedOperationException("`Verification of legacy transactions is not supported by MockServices. Use MockNode instead.")
|
||||
}
|
||||
|
||||
|
||||
@CordaInternal
|
||||
internal class MockAppServiceHubImpl<out T : SerializeAsToken>(serviceHub: MockServices, serviceConstructor: (AppServiceHub) -> T) :
|
||||
AppServiceHub, VerifyingServiceHub by serviceHub.verifyingView {
|
||||
internal val serviceInstance: T = serviceConstructor(this)
|
||||
|
||||
init {
|
||||
serviceHub.cordappServices.putInstance(serviceInstance.javaClass, serviceInstance)
|
||||
@ -576,5 +606,11 @@ fun <T : SerializeAsToken> createMockCordaService(serviceHub: MockServices, serv
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
}
|
||||
return MockAppServiceHubImpl(serviceHub, serviceConstructor).serviceInstance
|
||||
}
|
||||
|
||||
/**
|
||||
* Function which can be used to create a mock [CordaService] for use within testing, such as an Oracle.
|
||||
*/
|
||||
fun <T : SerializeAsToken> createMockCordaService(serviceHub: MockServices, serviceConstructor: (AppServiceHub) -> T): T {
|
||||
return MockServices.MockAppServiceHubImpl(serviceHub, serviceConstructor).serviceInstance
|
||||
}
|
||||
|
@ -3,7 +3,10 @@ package net.corda.testing.node
|
||||
import net.corda.core.DoNotImplement
|
||||
import net.corda.testing.driver.DriverParameters
|
||||
import net.corda.testing.driver.NodeParameters
|
||||
import net.corda.testing.node.internal.TestCordappImpl
|
||||
import net.corda.testing.node.internal.ScanPackageTestCordapp
|
||||
import net.corda.testing.node.internal.UriTestCordapp
|
||||
import java.net.URI
|
||||
import java.nio.file.Path
|
||||
|
||||
/**
|
||||
* Encapsulates a CorDapp that exists on the current classpath, which can be pulled in for testing. Use [TestCordapp.findCordapp]
|
||||
@ -25,6 +28,12 @@ abstract class TestCordapp {
|
||||
/** Returns a copy of this [TestCordapp] but with the specified CorDapp config. */
|
||||
abstract fun withConfig(config: Map<String, Any>): TestCordapp
|
||||
|
||||
/**
|
||||
* Returns a copy of this [TestCordapp] signed with a development signing key. The same signing key will be used for all signed
|
||||
* [TestCordapp]s. If the CorDapp jar is already signed, then the new jar created will its signing key replaced by the development key.
|
||||
*/
|
||||
abstract fun asSigned(): TestCordapp
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Scans the current classpath to find the CorDapp that contains the given package. All the CorDapp's metdata present in its
|
||||
@ -34,6 +43,14 @@ abstract class TestCordapp {
|
||||
* @param scanPackage The package name used to find the CorDapp. This does not need to be the root package of the CorDapp.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun findCordapp(scanPackage: String): TestCordapp = TestCordappImpl(scanPackage = scanPackage, config = emptyMap())
|
||||
fun findCordapp(scanPackage: String): TestCordapp = ScanPackageTestCordapp(scanPackage)
|
||||
|
||||
/**
|
||||
* [URI] location to a CorDapp jar. This may be a path on the local file system or a URL to an external resource.
|
||||
*
|
||||
* A [Path] can be converted into a [URI] with [Path.toUri].
|
||||
*/
|
||||
@JvmStatic
|
||||
fun of(uri: URI): TestCordapp = UriTestCordapp(uri)
|
||||
}
|
||||
}
|
||||
|
@ -1,27 +1,29 @@
|
||||
package net.corda.testing.node.internal
|
||||
|
||||
import io.github.classgraph.ClassGraph
|
||||
import net.corda.core.internal.*
|
||||
import net.corda.core.internal.PLATFORM_VERSION
|
||||
import net.corda.core.internal.VisibleForTesting
|
||||
import net.corda.core.internal.cordapp.CordappImpl
|
||||
import net.corda.core.internal.cordapp.set
|
||||
import net.corda.core.internal.pooledScan
|
||||
import net.corda.core.node.services.AttachmentFixup
|
||||
import net.corda.core.serialization.SerializationWhitelist
|
||||
import net.corda.core.utilities.contextLogger
|
||||
import net.corda.core.utilities.debug
|
||||
import net.corda.testing.core.internal.JarSignatureTestUtils.generateKey
|
||||
import net.corda.testing.core.internal.JarSignatureTestUtils.containsKey
|
||||
import net.corda.testing.core.internal.JarSignatureTestUtils.signJar
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import java.nio.file.attribute.FileTime
|
||||
import java.time.Instant
|
||||
import java.util.*
|
||||
import java.util.UUID
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.jar.Attributes
|
||||
import java.util.jar.JarFile
|
||||
import java.util.jar.JarOutputStream
|
||||
import java.util.jar.Manifest
|
||||
import java.util.zip.ZipEntry
|
||||
import kotlin.io.path.createDirectories
|
||||
import kotlin.io.path.div
|
||||
import kotlin.io.path.outputStream
|
||||
|
||||
/**
|
||||
* Represents a completely custom CorDapp comprising of resources taken from packages on the existing classpath, even including individual
|
||||
@ -44,6 +46,8 @@ data class CustomCordapp(
|
||||
|
||||
override fun withOnlyJarContents(): CustomCordapp = CustomCordapp(packages = packages, classes = classes, fixups = fixups)
|
||||
|
||||
override fun asSigned(): CustomCordapp = signed()
|
||||
|
||||
fun signed(keyStorePath: Path? = null, numberOfSignatures: Int = 1, keyAlgorithm: String = "RSA"): CustomCordapp =
|
||||
copy(signingInfo = SigningInfo(keyStorePath, numberOfSignatures, keyAlgorithm))
|
||||
|
||||
@ -109,23 +113,6 @@ data class CustomCordapp(
|
||||
}
|
||||
}
|
||||
|
||||
private fun signJar(jarFile: Path) {
|
||||
if (signingInfo != null) {
|
||||
val keyStorePathToUse = signingInfo.keyStorePath ?: defaultJarSignerDirectory.createDirectories()
|
||||
for (i in 1 .. signingInfo.numberOfSignatures) {
|
||||
val alias = "alias$i"
|
||||
val pwd = "secret!"
|
||||
if (!keyStorePathToUse.containsKey(alias, pwd)) {
|
||||
keyStorePathToUse.generateKey(alias, pwd, "O=Test Company Ltd $i,OU=Test,L=London,C=GB", signingInfo.keyAlgorithm)
|
||||
}
|
||||
val pk = keyStorePathToUse.signJar(jarFile.toString(), alias, pwd)
|
||||
logger.debug { "Signed Jar: $jarFile with public key $pk" }
|
||||
}
|
||||
} else {
|
||||
logger.debug { "Unsigned Jar: $jarFile" }
|
||||
}
|
||||
}
|
||||
|
||||
private fun createTestManifest(name: String, versionId: Int, targetPlatformVersion: Int): Manifest {
|
||||
val manifest = Manifest()
|
||||
|
||||
@ -155,13 +142,12 @@ data class CustomCordapp(
|
||||
}
|
||||
}
|
||||
|
||||
data class SigningInfo(val keyStorePath: Path?, val numberOfSignatures: Int, val keyAlgorithm: String)
|
||||
data class SigningInfo(val keyStorePath: Path?, val signatureCount: Int, val algorithm: String)
|
||||
|
||||
companion object {
|
||||
private val logger = contextLogger()
|
||||
private val epochFileTime = FileTime.from(Instant.EPOCH)
|
||||
private val cordappsDirectory: Path
|
||||
private val defaultJarSignerDirectory: Path
|
||||
private val whitespace = "\\s++".toRegex()
|
||||
private val cache = ConcurrentHashMap<CustomCordapp, Path>()
|
||||
|
||||
@ -169,7 +155,6 @@ data class CustomCordapp(
|
||||
val buildDir = Paths.get("build").toAbsolutePath()
|
||||
val timeDirName = getTimestampAsDirectoryName()
|
||||
cordappsDirectory = buildDir / "generated-custom-cordapps" / timeDirName
|
||||
defaultJarSignerDirectory = buildDir / "jar-signer" / timeDirName
|
||||
}
|
||||
|
||||
fun getJarFile(cordapp: CustomCordapp): Path {
|
||||
@ -179,10 +164,12 @@ data class CustomCordapp(
|
||||
val jarFile = cordappsDirectory.createDirectories() / filename
|
||||
if (it.fixups.isNotEmpty()) {
|
||||
it.createFixupJar(jarFile)
|
||||
} else if(it.packages.isNotEmpty() || it.classes.isNotEmpty() || it.fixups.isNotEmpty()) {
|
||||
it.packageAsJar(jarFile)
|
||||
} else if (it.packages.isNotEmpty() || it.classes.isNotEmpty()) {
|
||||
it.packageAsJar(jarFile)
|
||||
}
|
||||
if (it.signingInfo != null) {
|
||||
TestCordappSigner.signJar(jarFile, it.signingInfo.keyStorePath, it.signingInfo.signatureCount, it.signingInfo.algorithm)
|
||||
}
|
||||
it.signJar(jarFile)
|
||||
logger.debug { "$it packaged into $jarFile" }
|
||||
jarFile
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ import com.google.common.util.concurrent.ThreadFactoryBuilder
|
||||
import com.typesafe.config.Config
|
||||
import com.typesafe.config.ConfigFactory
|
||||
import com.typesafe.config.ConfigRenderOptions
|
||||
import com.typesafe.config.ConfigValue
|
||||
import com.typesafe.config.ConfigValueFactory
|
||||
import net.corda.client.rpc.CordaRPCClient
|
||||
import net.corda.client.rpc.RPCException
|
||||
@ -24,6 +23,7 @@ import net.corda.core.internal.concurrent.fork
|
||||
import net.corda.core.internal.concurrent.map
|
||||
import net.corda.core.internal.concurrent.openFuture
|
||||
import net.corda.core.internal.concurrent.transpose
|
||||
import net.corda.core.internal.copyToDirectory
|
||||
import net.corda.core.internal.cordapp.CordappImpl.Companion.CORDAPP_CONTRACT_LICENCE
|
||||
import net.corda.core.internal.cordapp.CordappImpl.Companion.CORDAPP_CONTRACT_NAME
|
||||
import net.corda.core.internal.cordapp.CordappImpl.Companion.CORDAPP_CONTRACT_VENDOR
|
||||
@ -35,24 +35,18 @@ import net.corda.core.internal.cordapp.CordappImpl.Companion.CORDAPP_WORKFLOW_VE
|
||||
import net.corda.core.internal.cordapp.CordappImpl.Companion.MIN_PLATFORM_VERSION
|
||||
import net.corda.core.internal.cordapp.CordappImpl.Companion.TARGET_PLATFORM_VERSION
|
||||
import net.corda.core.internal.cordapp.get
|
||||
import net.corda.core.internal.createDirectories
|
||||
import net.corda.core.internal.deleteIfExists
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.internal.isRegularFile
|
||||
import net.corda.core.internal.list
|
||||
import net.corda.core.internal.packageName_
|
||||
import net.corda.core.internal.readObject
|
||||
import net.corda.core.internal.readText
|
||||
import net.corda.core.internal.toPath
|
||||
import net.corda.core.internal.uncheckedCast
|
||||
import net.corda.core.internal.writeText
|
||||
import net.corda.core.messaging.CordaRPCOps
|
||||
import net.corda.core.node.NetworkParameters
|
||||
import net.corda.core.node.NotaryInfo
|
||||
import net.corda.core.node.services.NetworkMapCache
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.core.utilities.contextLogger
|
||||
import net.corda.core.utilities.debug
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import net.corda.core.utilities.millis
|
||||
import net.corda.core.utilities.toHexString
|
||||
import net.corda.coretesting.internal.stubs.CertificateStoreStubs
|
||||
@ -62,6 +56,7 @@ import net.corda.node.internal.DataSourceFactory
|
||||
import net.corda.node.internal.Node
|
||||
import net.corda.node.internal.NodeWithInfo
|
||||
import net.corda.node.internal.clientSslOptionsCompatibleWith
|
||||
import net.corda.node.internal.cordapp.JarScanningCordappLoader.Companion.LEGACY_CONTRACTS_DIR_NAME
|
||||
import net.corda.node.services.Permissions
|
||||
import net.corda.node.services.config.ConfigHelper
|
||||
import net.corda.node.services.config.FlowOverride
|
||||
@ -118,8 +113,9 @@ import java.time.Instant
|
||||
import java.time.ZoneOffset.UTC
|
||||
import java.time.ZonedDateTime
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.util.*
|
||||
import java.util.Collections.unmodifiableList
|
||||
import java.util.Random
|
||||
import java.util.UUID
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.ScheduledExecutorService
|
||||
import java.util.concurrent.TimeUnit
|
||||
@ -127,10 +123,15 @@ import java.util.concurrent.TimeoutException
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import java.util.jar.JarInputStream
|
||||
import java.util.jar.Manifest
|
||||
import kotlin.collections.ArrayList
|
||||
import kotlin.collections.HashMap
|
||||
import kotlin.collections.HashSet
|
||||
import kotlin.concurrent.thread
|
||||
import kotlin.io.path.createDirectories
|
||||
import kotlin.io.path.deleteIfExists
|
||||
import kotlin.io.path.div
|
||||
import kotlin.io.path.isRegularFile
|
||||
import kotlin.io.path.name
|
||||
import kotlin.io.path.readText
|
||||
import kotlin.io.path.useDirectoryEntries
|
||||
import kotlin.io.path.writeText
|
||||
import net.corda.nodeapi.internal.config.User as InternalUser
|
||||
|
||||
class DriverDSLImpl(
|
||||
@ -266,7 +267,7 @@ class DriverDSLImpl(
|
||||
override fun startNode(parameters: NodeParameters, bytemanPort: Int?): CordaFuture<NodeHandle> {
|
||||
val p2pAddress = portAllocation.nextHostAndPort()
|
||||
// TODO: Derive name from the full picked name, don't just wrap the common name
|
||||
val name = parameters.providedName ?: CordaX500Name("${oneOf(names).organisation}-${p2pAddress.port}", "London", "GB")
|
||||
val name = parameters.providedName ?: CordaX500Name("${names.random().organisation}-${p2pAddress.port}", "London", "GB")
|
||||
|
||||
val config = createConfig(name, parameters, p2pAddress)
|
||||
if (isH2Database(config) && !inMemoryDB) {
|
||||
@ -414,11 +415,11 @@ class DriverDSLImpl(
|
||||
|
||||
while (process.isAlive) try {
|
||||
val response = client.newCall(Request.Builder().url(url).build()).execute()
|
||||
if (response.isSuccessful && (response.body()?.string() == "started")) {
|
||||
if (response.isSuccessful && (response.body?.string() == "started")) {
|
||||
return WebserverHandle(handle.webAddress, process)
|
||||
}
|
||||
} catch (e: ConnectException) {
|
||||
log.debug("Retrying webserver info at ${handle.webAddress}")
|
||||
log.debug { "Retrying webserver info at ${handle.webAddress}" }
|
||||
}
|
||||
|
||||
throw IllegalStateException("Webserver at ${handle.webAddress} has died")
|
||||
@ -577,9 +578,8 @@ class DriverDSLImpl(
|
||||
// This causes two node info files to be generated.
|
||||
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()
|
||||
val nodeInfoFile = config.corda.baseDirectory.useDirectoryEntries { paths ->
|
||||
paths.single { it.name.startsWith(NodeInfoFilesCopier.NODE_INFO_FILE_NAME_PREFIX) }
|
||||
}
|
||||
val nodeInfo = nodeInfoFile.readObject<SignedNodeInfo>().verified()
|
||||
Pair(config.withNotaryDefinition(spec.validating), NotaryInfo(nodeInfo.legalIdentities[0], spec.validating))
|
||||
@ -718,6 +718,11 @@ class DriverDSLImpl(
|
||||
extraCustomCordapps + (cordappsForAllNodes ?: emptySet())
|
||||
)
|
||||
|
||||
if (parameters.legacyContracts.isNotEmpty()) {
|
||||
val legacyContractsDir = (baseDirectory / LEGACY_CONTRACTS_DIR_NAME).createDirectories()
|
||||
parameters.legacyContracts.forEach { (it as TestCordappInternal).jarFile.copyToDirectory(legacyContractsDir) }
|
||||
}
|
||||
|
||||
val nodeFuture = if (parameters.startInSameProcess ?: startNodesInProcess) {
|
||||
val nodeAndThreadFuture = startInProcessNode(executorService, config, allowHibernateToManageAppSchema)
|
||||
shutdownManager.registerShutdown(
|
||||
@ -848,7 +853,7 @@ class DriverDSLImpl(
|
||||
|
||||
companion object {
|
||||
private val RPC_CONNECT_POLL_INTERVAL: Duration = 100.millis
|
||||
internal val log = contextLogger()
|
||||
private val log = contextLogger()
|
||||
|
||||
// While starting with inProcess mode, we need to have different names to avoid clashes
|
||||
private val inMemoryCounter = AtomicInteger()
|
||||
@ -890,18 +895,6 @@ class DriverDSLImpl(
|
||||
CORDAPP_WORKFLOW_VERSION
|
||||
))
|
||||
|
||||
private inline fun <T> Config.withOptionalValue(key: String, obj: T?, body: (T) -> ConfigValue): Config {
|
||||
return if (obj == null) {
|
||||
this
|
||||
} else {
|
||||
withValue(key, body(obj))
|
||||
}
|
||||
}
|
||||
|
||||
private fun <T> valueFor(any: T): ConfigValue = ConfigValueFactory.fromAnyRef(any)
|
||||
|
||||
private fun <A> oneOf(array: Array<A>) = array[Random().nextInt(array.size)]
|
||||
|
||||
private fun startInProcessNode(
|
||||
executorService: ScheduledExecutorService,
|
||||
config: NodeConfig,
|
||||
@ -969,15 +962,15 @@ class DriverDSLImpl(
|
||||
val excludePackagePattern = "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**;" +
|
||||
"io.github**;io.netty**;jdk**;joptsimple**;junit**;kotlin**;net.bytebuddy**;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 excludeClassloaderPattern = "l(net.corda.core.serialization.internal.**)"
|
||||
val quasarOptions = "m"
|
||||
val extraJvmArguments = systemProperties.removeResolvedClasspath().map { "-D${it.key}=${it.value}" } +
|
||||
"-javaagent:$quasarJarPath=$excludePackagePattern$excludeClassloaderPattern"
|
||||
"-javaagent:$quasarJarPath=$quasarOptions$excludePackagePattern$excludeClassloaderPattern"
|
||||
|
||||
val loggingLevel = when {
|
||||
logLevelOverride != null -> logLevelOverride
|
||||
@ -992,26 +985,27 @@ class DriverDSLImpl(
|
||||
it.addAll(extraCmdLineFlag)
|
||||
}.toList()
|
||||
|
||||
val bytemanJvmArgs = {
|
||||
val bytemanAgent = bytemanJarPath?.let {
|
||||
bytemanPort?.let {
|
||||
"-javaagent:$bytemanJarPath=port:$bytemanPort,listener:true"
|
||||
}
|
||||
val bytemanAgent = bytemanJarPath?.let {
|
||||
bytemanPort?.let {
|
||||
"-javaagent:$bytemanJarPath=port:$bytemanPort,listener:true"
|
||||
}
|
||||
listOfNotNull(bytemanAgent) +
|
||||
if (bytemanAgent != null && debugPort != null) listOf(
|
||||
}
|
||||
val bytemanJvmArgs = listOfNotNull(bytemanAgent) +
|
||||
if (bytemanAgent != null && debugPort != null) {
|
||||
listOf(
|
||||
"-Dorg.jboss.byteman.verbose=true",
|
||||
"-Dorg.jboss.byteman.debug=true"
|
||||
)
|
||||
else emptyList()
|
||||
}.invoke()
|
||||
} else {
|
||||
emptyList()
|
||||
}
|
||||
|
||||
// The following dependencies are excluded from the classpath of the created JVM,
|
||||
// so that the environment resembles a real one as close as possible.
|
||||
val cp = ProcessUtilities.defaultClassPath.filter { cpEntry ->
|
||||
val cpPathEntry = Paths.get(cpEntry)
|
||||
cpPathEntry.isRegularFile()
|
||||
&& !isTestArtifact(cpPathEntry.fileName.toString())
|
||||
&& !isTestArtifact(cpPathEntry.name)
|
||||
&& !cpPathEntry.isExcludedJar
|
||||
}
|
||||
|
||||
@ -1019,7 +1013,7 @@ class DriverDSLImpl(
|
||||
className = "net.corda.node.Corda", // cannot directly get class for this, so just use string
|
||||
arguments = arguments,
|
||||
jdwpPort = debugPort,
|
||||
extraJvmArguments = extraJvmArguments + bytemanJvmArgs + "-Dnet.corda.node.printErrorsToStdErr=true",
|
||||
extraJvmArguments = extraJvmArguments + bytemanJvmArgs + nodeJvmArgs + "-Dnet.corda.node.printErrorsToStdErr=true",
|
||||
workingDirectory = config.corda.baseDirectory,
|
||||
maximumHeapSize = maximumHeapSize,
|
||||
classPath = cp,
|
||||
@ -1066,10 +1060,10 @@ class DriverDSLImpl(
|
||||
}
|
||||
|
||||
private fun startWebserver(handle: NodeHandleInternal, debugPort: Int?, maximumHeapSize: String): Process {
|
||||
val className = "net.corda.webserver.WebServer"
|
||||
writeConfig(handle.baseDirectory, "web-server.conf", handle.toWebServerConfig())
|
||||
return ProcessUtilities.startJavaProcess(
|
||||
className = className, // cannot directly get class for this, so just use string
|
||||
className = "net.corda.webserver.WebServer", // cannot directly get class for this, so just use string
|
||||
workingDirectory = handle.baseDirectory,
|
||||
arguments = listOf(BASE_DIR, handle.baseDirectory.toString()),
|
||||
jdwpPort = debugPort,
|
||||
extraJvmArguments = listOf("-Dname=node-${handle.p2pAddress}-webserver") +
|
||||
@ -1092,12 +1086,11 @@ class DriverDSLImpl(
|
||||
}
|
||||
|
||||
private fun NodeHandleInternal.toWebServerConfig(): Config {
|
||||
|
||||
var config = ConfigFactory.empty()
|
||||
config += "webAddress" to webAddress.toString()
|
||||
config += "myLegalName" to configuration.myLegalName.toString()
|
||||
config += "rpcAddress" to configuration.rpcOptions.address.toString()
|
||||
config += "rpcUsers" to configuration.toConfig().getValue("rpcUsers")
|
||||
config += "rpcUsers" to configuration.rpcUsers.map { it.toConfig().root().unwrapped() }
|
||||
config += "useHTTPS" to useHTTPS
|
||||
config += "baseDirectory" to configuration.baseDirectory.toAbsolutePath().toString()
|
||||
|
||||
@ -1111,7 +1104,7 @@ class DriverDSLImpl(
|
||||
}
|
||||
|
||||
private fun createCordappsClassLoader(cordapps: Collection<TestCordappInternal>?): URLClassLoader? {
|
||||
if (cordapps == null || cordapps.isEmpty()) {
|
||||
if (cordapps.isNullOrEmpty()) {
|
||||
return null
|
||||
}
|
||||
return URLClassLoader(cordapps.map { it.jarFile.toUri().toURL() }.toTypedArray())
|
||||
@ -1267,59 +1260,7 @@ fun <DI : DriverDSL, D : InternalDriverDSL, A> genericDriver(
|
||||
driverDsl.start()
|
||||
return dsl(coerce(driverDsl))
|
||||
} catch (exception: Throwable) {
|
||||
DriverDSLImpl.log.error("Driver shutting down because of exception", exception)
|
||||
throw exception
|
||||
} finally {
|
||||
driverDsl.shutdown()
|
||||
shutdownHook.cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a helper method to allow extending of the DSL, along the lines of
|
||||
* interface SomeOtherExposedDSLInterface : DriverDSL
|
||||
* interface SomeOtherInternalDSLInterface : InternalDriverDSL, SomeOtherExposedDSLInterface
|
||||
* class SomeOtherDSL(val driverDSL : DriverDSLImpl) : InternalDriverDSL by driverDSL, SomeOtherInternalDSLInterface
|
||||
*
|
||||
* @param coerce We need this explicit coercion witness because we can't put an extra DI : D bound in a `where` clause.
|
||||
*/
|
||||
fun <DI : DriverDSL, D : InternalDriverDSL, A> genericDriver(
|
||||
defaultParameters: DriverParameters = DriverParameters(),
|
||||
driverDslWrapper: (DriverDSLImpl) -> D,
|
||||
coerce: (D) -> DI, dsl: DI.() -> A
|
||||
): A {
|
||||
setDriverSerialization().use { _ ->
|
||||
val driverDsl = driverDslWrapper(
|
||||
DriverDSLImpl(
|
||||
portAllocation = defaultParameters.portAllocation,
|
||||
debugPortAllocation = defaultParameters.debugPortAllocation,
|
||||
systemProperties = defaultParameters.systemProperties,
|
||||
driverDirectory = defaultParameters.driverDirectory.toAbsolutePath(),
|
||||
useTestClock = defaultParameters.useTestClock,
|
||||
isDebug = defaultParameters.isDebug,
|
||||
startNodesInProcess = defaultParameters.startNodesInProcess,
|
||||
waitForAllNodesToFinish = defaultParameters.waitForAllNodesToFinish,
|
||||
extraCordappPackagesToScan = @Suppress("DEPRECATION") defaultParameters.extraCordappPackagesToScan,
|
||||
jmxPolicy = defaultParameters.jmxPolicy,
|
||||
notarySpecs = defaultParameters.notarySpecs,
|
||||
compatibilityZone = null,
|
||||
networkParameters = defaultParameters.networkParameters,
|
||||
notaryCustomOverrides = defaultParameters.notaryCustomOverrides,
|
||||
inMemoryDB = defaultParameters.inMemoryDB,
|
||||
cordappsForAllNodes = uncheckedCast(defaultParameters.cordappsForAllNodes),
|
||||
environmentVariables = defaultParameters.environmentVariables,
|
||||
allowHibernateToManageAppSchema = defaultParameters.allowHibernateToManageAppSchema,
|
||||
premigrateH2Database = defaultParameters.premigrateH2Database,
|
||||
notaryHandleTimeout = defaultParameters.notaryHandleTimeout
|
||||
)
|
||||
)
|
||||
val shutdownHook = addShutdownHook(driverDsl::shutdown)
|
||||
try {
|
||||
driverDsl.start()
|
||||
return dsl(coerce(driverDsl))
|
||||
} catch (exception: Throwable) {
|
||||
DriverDSLImpl.log.error("Driver shutting down because of exception", exception)
|
||||
loggerFor<DriverDSL>().error("Driver shutting down because of exception", exception)
|
||||
throw exception
|
||||
} finally {
|
||||
driverDsl.shutdown()
|
||||
@ -1334,7 +1275,7 @@ fun <DI : DriverDSL, D : InternalDriverDSL, A> genericDriver(
|
||||
* @property publishNotaries Hook for a network map server to capture the generated [NotaryInfo] objects needed for
|
||||
* creating the network parameters. This is needed as the network map server is expected to distribute it. The callback
|
||||
* will occur on a different thread to the driver-calling thread.
|
||||
* @property rootCert If specified then the nodes will register themselves with the doorman service using [url] and expect
|
||||
* @property rootCert If specified then the nodes will register themselves with the doorman service using [SharedCompatibilityZoneParams.url] and expect
|
||||
* the registration response to be rooted at this cert. If not specified then no registration is performed and the dev
|
||||
* root cert is used as normal.
|
||||
*
|
||||
|
@ -1,7 +1,5 @@
|
||||
package net.corda.testing.node.internal
|
||||
|
||||
import com.nhaarman.mockito_kotlin.doReturn
|
||||
import com.nhaarman.mockito_kotlin.whenever
|
||||
import net.corda.common.configuration.parsing.internal.ConfigurationWithOptions
|
||||
import net.corda.core.DoNotImplement
|
||||
import net.corda.core.crypto.SecureHash
|
||||
@ -15,10 +13,8 @@ import net.corda.core.internal.FlowIORequest
|
||||
import net.corda.core.internal.NetworkParametersStorage
|
||||
import net.corda.core.internal.PLATFORM_VERSION
|
||||
import net.corda.core.internal.VisibleForTesting
|
||||
import net.corda.core.internal.createDirectories
|
||||
import net.corda.core.internal.deleteIfExists
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.internal.notary.NotaryService
|
||||
import net.corda.core.internal.telemetry.TelemetryServiceImpl
|
||||
import net.corda.core.internal.uncheckedCast
|
||||
import net.corda.core.messaging.CordaRPCOps
|
||||
import net.corda.core.messaging.MessageRecipients
|
||||
@ -27,7 +23,6 @@ import net.corda.core.messaging.SingleMessageRecipient
|
||||
import net.corda.core.node.NetworkParameters
|
||||
import net.corda.core.node.NodeInfo
|
||||
import net.corda.core.node.NotaryInfo
|
||||
import net.corda.core.internal.telemetry.TelemetryServiceImpl
|
||||
import net.corda.core.serialization.SerializationWhitelist
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.core.utilities.contextLogger
|
||||
@ -74,6 +69,8 @@ import net.corda.testing.node.MockServices.Companion.makeTestDataSourcePropertie
|
||||
import net.corda.testing.node.TestClock
|
||||
import org.apache.activemq.artemis.utils.ReusableLatch
|
||||
import org.apache.sshd.common.util.security.SecurityUtils
|
||||
import org.mockito.kotlin.doReturn
|
||||
import org.mockito.kotlin.whenever
|
||||
import rx.Observable
|
||||
import rx.Scheduler
|
||||
import rx.internal.schedulers.CachedThreadScheduler
|
||||
@ -85,6 +82,9 @@ import java.time.Clock
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
import kotlin.io.path.createDirectories
|
||||
import kotlin.io.path.deleteIfExists
|
||||
import kotlin.io.path.div
|
||||
|
||||
val MOCK_VERSION_INFO = VersionInfo(PLATFORM_VERSION, "Mock release", "Mock revision", "Mock Vendor")
|
||||
|
||||
@ -116,9 +116,6 @@ data class InternalMockNodeParameters(
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A [StartedNode] which exposes its internal [InternalMockNetwork.MockNode] for testing.
|
||||
*/
|
||||
interface TestStartedNode {
|
||||
val internals: InternalMockNetwork.MockNode
|
||||
val info: NodeInfo
|
||||
@ -170,7 +167,7 @@ open class InternalMockNetwork(cordappPackages: List<String> = emptyList(),
|
||||
val autoVisibleNodes: Boolean = true) : AutoCloseable {
|
||||
companion object {
|
||||
fun createCordappClassLoader(cordapps: Collection<TestCordappInternal>?): URLClassLoader? {
|
||||
if (cordapps == null || cordapps.isEmpty()) {
|
||||
if (cordapps.isNullOrEmpty()) {
|
||||
return null
|
||||
}
|
||||
return URLClassLoader(cordapps.map { it.jarFile.toUri().toURL() }.toTypedArray())
|
||||
@ -352,7 +349,6 @@ open class InternalMockNetwork(cordappPackages: List<String> = emptyList(),
|
||||
|
||||
private val entropyCounter = AtomicReference(args.entropyRoot)
|
||||
override val log get() = staticLog
|
||||
override val transactionVerifierWorkerCount: Int get() = 1
|
||||
|
||||
private var _rxIoScheduler: Scheduler? = null
|
||||
override val rxIoScheduler: Scheduler
|
||||
@ -455,18 +451,15 @@ open class InternalMockNetwork(cordappPackages: List<String> = emptyList(),
|
||||
return if (track) {
|
||||
smm.changes.filter { it is StateMachineManager.Change.Add }.map { it.logic }.ofType(initiatedFlowClass)
|
||||
} else {
|
||||
Observable.empty<T>()
|
||||
Observable.empty()
|
||||
}
|
||||
}
|
||||
|
||||
override fun makeNetworkParametersStorage(): NetworkParametersStorage = MockNetworkParametersStorage()
|
||||
}
|
||||
|
||||
fun createUnstartedNode(parameters: InternalMockNodeParameters = InternalMockNodeParameters()): MockNode {
|
||||
return createUnstartedNode(parameters, defaultFactory)
|
||||
}
|
||||
|
||||
fun createUnstartedNode(parameters: InternalMockNodeParameters = InternalMockNodeParameters(), nodeFactory: (MockNodeArgs) -> MockNode): MockNode {
|
||||
fun createUnstartedNode(parameters: InternalMockNodeParameters = InternalMockNodeParameters(),
|
||||
nodeFactory: (MockNodeArgs) -> MockNode = defaultFactory): MockNode {
|
||||
return createNodeImpl(parameters, nodeFactory, false)
|
||||
}
|
||||
|
||||
@ -681,16 +674,17 @@ private fun mockNodeConfiguration(certificatesDirectory: Path): NodeConfiguratio
|
||||
}
|
||||
|
||||
class MockNodeFlowManager : NodeFlowManager() {
|
||||
val testingRegistrations = HashMap<Class<out FlowLogic<*>>, InitiatedFlowFactory<*>>()
|
||||
private val testingRegistrations = HashMap<Class<out FlowLogic<*>>, InitiatedFlowFactory<*>>()
|
||||
|
||||
override fun getFlowFactoryForInitiatingFlow(initiatedFlowClass: Class<out FlowLogic<*>>): InitiatedFlowFactory<*>? {
|
||||
if (initiatedFlowClass in testingRegistrations) {
|
||||
return testingRegistrations.get(initiatedFlowClass)
|
||||
return testingRegistrations[initiatedFlowClass]
|
||||
}
|
||||
return super.getFlowFactoryForInitiatingFlow(initiatedFlowClass)
|
||||
}
|
||||
|
||||
fun registerTestingFactory(initiator: Class<out FlowLogic<*>>, factory: InitiatedFlowFactory<*>) {
|
||||
testingRegistrations.put(initiator, factory)
|
||||
testingRegistrations[initiator] = factory
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
package net.corda.testing.node.internal
|
||||
|
||||
import com.nhaarman.mockito_kotlin.doReturn
|
||||
import com.nhaarman.mockito_kotlin.whenever
|
||||
import org.mockito.kotlin.doReturn
|
||||
import org.mockito.kotlin.whenever
|
||||
import net.corda.node.services.config.FlowTimeoutConfiguration
|
||||
import net.corda.node.services.config.NodeConfiguration
|
||||
import net.corda.node.services.config.NotaryConfig
|
||||
@ -18,4 +18,4 @@ fun MockNodeConfigOverrides.applyMockNodeOverrides(config: NodeConfiguration) {
|
||||
this.extraDataSourceProperties?.forEach { k, v -> it.dataSourceProperties.put(k, v) }
|
||||
this.flowTimeout?.also { fto -> doReturn(FlowTimeoutConfiguration(fto.timeout, fto.maxRestartCount, fto.backoffBase)).whenever(config).flowTimeout }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,8 +10,6 @@ import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.FlowStateMachineHandle
|
||||
import net.corda.core.internal.VisibleForTesting
|
||||
import net.corda.core.internal.concurrent.openFuture
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.internal.readText
|
||||
import net.corda.core.internal.times
|
||||
import net.corda.core.messaging.CordaRPCOps
|
||||
import net.corda.core.node.services.AttachmentFixup
|
||||
@ -22,14 +20,14 @@ import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.core.utilities.millis
|
||||
import net.corda.core.utilities.seconds
|
||||
import net.corda.coretesting.internal.createTestSerializationEnv
|
||||
import net.corda.coretesting.internal.inVMExecutors
|
||||
import net.corda.node.services.api.StartedNodeServices
|
||||
import net.corda.node.services.messaging.Message
|
||||
import net.corda.node.services.statemachine.Checkpoint
|
||||
import net.corda.testing.driver.DriverDSL
|
||||
import net.corda.testing.driver.NodeHandle
|
||||
import net.corda.testing.internal.chooseIdentity
|
||||
import net.corda.coretesting.internal.createTestSerializationEnv
|
||||
import net.corda.coretesting.internal.inVMExecutors
|
||||
import net.corda.testing.node.InMemoryMessagingNetwork
|
||||
import net.corda.testing.node.TestCordapp
|
||||
import net.corda.testing.node.User
|
||||
@ -50,6 +48,8 @@ import java.util.concurrent.ScheduledExecutorService
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.jar.JarOutputStream
|
||||
import java.util.zip.ZipEntry
|
||||
import kotlin.io.path.div
|
||||
import kotlin.io.path.readText
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
private val log = LoggerFactory.getLogger("net.corda.testing.internal.InternalTestUtils")
|
||||
@ -61,7 +61,7 @@ private val log = LoggerFactory.getLogger("net.corda.testing.internal.InternalTe
|
||||
* You will probably need to use [FINANCE_CORDAPPS] instead to get access to the flows as well.
|
||||
*/
|
||||
@JvmField
|
||||
val FINANCE_CONTRACTS_CORDAPP: TestCordappImpl = findCordapp("net.corda.finance.contracts")
|
||||
val FINANCE_CONTRACTS_CORDAPP: ScanPackageTestCordapp = findCordapp("net.corda.finance.contracts")
|
||||
|
||||
/**
|
||||
* Reference to the finance-workflows CorDapp in this repo. The metadata is taken directly from finance/workflows/build.gradle, including the
|
||||
@ -70,10 +70,10 @@ val FINANCE_CONTRACTS_CORDAPP: TestCordappImpl = findCordapp("net.corda.finance.
|
||||
* You will probably need to use [FINANCE_CORDAPPS] instead to get access to the contract classes as well.
|
||||
*/
|
||||
@JvmField
|
||||
val FINANCE_WORKFLOWS_CORDAPP: TestCordappImpl = findCordapp("net.corda.finance.workflows")
|
||||
val FINANCE_WORKFLOWS_CORDAPP: ScanPackageTestCordapp = findCordapp("net.corda.finance.workflows")
|
||||
|
||||
@JvmField
|
||||
val FINANCE_CORDAPPS: Set<TestCordappImpl> = setOf(FINANCE_CONTRACTS_CORDAPP, FINANCE_WORKFLOWS_CORDAPP)
|
||||
val FINANCE_CORDAPPS: Set<ScanPackageTestCordapp> = setOf(FINANCE_CONTRACTS_CORDAPP, FINANCE_WORKFLOWS_CORDAPP)
|
||||
|
||||
/**
|
||||
* *Custom* CorDapp containing the contents of the `net.corda.testing.contracts` package, i.e. the dummy contracts. This is not a real CorDapp
|
||||
@ -105,9 +105,9 @@ fun cordappWithFixups(fixups: List<AttachmentFixup>) = CustomCordapp(fixups = fi
|
||||
|
||||
/**
|
||||
* Find the single CorDapp jar on the current classpath which contains the given package. This is a convenience method for
|
||||
* [TestCordapp.findCordapp] but returns the internal [TestCordappImpl].
|
||||
* [TestCordapp.findCordapp] but returns the internal [ScanPackageTestCordapp].
|
||||
*/
|
||||
fun findCordapp(scanPackage: String): TestCordappImpl = TestCordapp.findCordapp(scanPackage) as TestCordappImpl
|
||||
fun findCordapp(scanPackage: String): ScanPackageTestCordapp = TestCordapp.findCordapp(scanPackage) as ScanPackageTestCordapp
|
||||
|
||||
/** Create a *custom* CorDapp which just contains the enclosed classes of the receiver class. */
|
||||
fun Any.enclosedCordapp(): CustomCordapp {
|
||||
@ -169,8 +169,7 @@ fun addressMustBeBoundFuture(executorService: ScheduledExecutorService, hostAndP
|
||||
}
|
||||
try {
|
||||
Socket(hostAndPort.host, hostAndPort.port).close()
|
||||
Unit
|
||||
} catch (_exception: SocketException) {
|
||||
} catch (_: SocketException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
@ -188,7 +187,7 @@ fun nodeMustBeStartedFuture(
|
||||
throw exception()
|
||||
}
|
||||
when {
|
||||
logFile.readText().contains("Running P2PMessaging loop") -> {
|
||||
"Running P2PMessaging loop" in logFile.readText() -> {
|
||||
Unit
|
||||
}
|
||||
Instant.now().isAfter(stopPolling) -> {
|
||||
@ -217,9 +216,7 @@ fun addressMustNotBeBoundFuture(executorService: ScheduledExecutorService, hostA
|
||||
try {
|
||||
Socket(hostAndPort.host, hostAndPort.port).close()
|
||||
null
|
||||
} catch (_exception: SocketException) {
|
||||
Unit
|
||||
}
|
||||
} catch (_: SocketException) { }
|
||||
}
|
||||
}
|
||||
|
||||
@ -304,6 +301,10 @@ fun DriverDSL.assertUncompletedCheckpoints(name: CordaX500Name, expected: Long)
|
||||
}
|
||||
}
|
||||
|
||||
val nodeJvmArgs: List<String> by lazy {
|
||||
DriverDSLImpl::class.java.getResourceAsStream("node-jvm-args.txt")!!.use { it.bufferedReader().readLines() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Should only be used by Driver and MockNode.
|
||||
*/
|
||||
|
@ -5,26 +5,27 @@ import net.corda.core.identity.Party
|
||||
import net.corda.core.internal.PLATFORM_VERSION
|
||||
import net.corda.core.internal.concurrent.fork
|
||||
import net.corda.core.internal.concurrent.transpose
|
||||
import net.corda.core.internal.createDirectories
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.node.NodeInfo
|
||||
import net.corda.core.node.NotaryInfo
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.coretesting.internal.testThreadFactory
|
||||
import net.corda.node.VersionInfo
|
||||
import net.corda.node.internal.FlowManager
|
||||
import net.corda.node.internal.Node
|
||||
import net.corda.node.internal.NodeFlowManager
|
||||
import net.corda.node.internal.NodeWithInfo
|
||||
import net.corda.node.services.config.*
|
||||
import net.corda.node.services.config.ConfigHelper
|
||||
import net.corda.node.services.config.FlowOverrideConfig
|
||||
import net.corda.node.services.config.NodeConfiguration
|
||||
import net.corda.node.services.config.configOf
|
||||
import net.corda.node.services.config.parseAsNodeConfiguration
|
||||
import net.corda.node.services.config.plus
|
||||
import net.corda.nodeapi.internal.DevIdentityGenerator
|
||||
import net.corda.nodeapi.internal.config.toConfig
|
||||
import net.corda.nodeapi.internal.network.NetworkParametersCopier
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import net.corda.testing.core.SerializationEnvironmentRule
|
||||
import net.corda.testing.driver.internal.incrementalPortAllocation
|
||||
import net.corda.coretesting.internal.testThreadFactory
|
||||
import net.corda.testing.node.User
|
||||
import org.apache.commons.lang3.SystemUtils
|
||||
import org.apache.logging.log4j.Level
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
@ -34,7 +35,8 @@ import rx.internal.schedulers.CachedThreadScheduler
|
||||
import java.nio.file.Path
|
||||
import java.util.concurrent.Executors
|
||||
import kotlin.concurrent.thread
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.io.path.createDirectories
|
||||
import kotlin.io.path.div
|
||||
|
||||
// TODO Some of the logic here duplicates what's in the driver - the reason why it's not straightforward to replace it by
|
||||
// using DriverDSLImpl in `init()` and `stopAllNodes()` is because of the platform version passed to nodes (driver doesn't
|
||||
@ -60,7 +62,7 @@ abstract class NodeBasedTest @JvmOverloads constructor(
|
||||
private val portAllocation = incrementalPortAllocation()
|
||||
|
||||
init {
|
||||
System.setProperty("consoleLogLevel", Level.DEBUG.name().toLowerCase())
|
||||
System.setProperty("consoleLogLevel", Level.DEBUG.name().lowercase())
|
||||
}
|
||||
|
||||
@Before
|
||||
@ -161,12 +163,9 @@ class InProcessNode(
|
||||
configuration: NodeConfiguration,
|
||||
versionInfo: VersionInfo,
|
||||
flowManager: FlowManager = NodeFlowManager(configuration.flowOverrides),
|
||||
allowHibernateToManageAppSchema: Boolean = true) : Node(configuration, versionInfo, false, flowManager = flowManager, allowHibernateToManageAppSchema = allowHibernateToManageAppSchema) {
|
||||
allowHibernateToManageAppSchema: Boolean = true
|
||||
) : Node(configuration, versionInfo, false, flowManager = flowManager, allowHibernateToManageAppSchema = allowHibernateToManageAppSchema) {
|
||||
override val runMigrationScripts: Boolean = true
|
||||
override fun start(): NodeInfo {
|
||||
assertFalse(isInvalidJavaVersion(), "You are using a version of Java that is not supported (${SystemUtils.JAVA_VERSION}). Please upgrade to the latest version of Java 8.")
|
||||
return super.start()
|
||||
}
|
||||
|
||||
override val rxIoScheduler get() = CachedThreadScheduler(testThreadFactory()).also { runOnStop += it::shutdown }
|
||||
|
||||
|
@ -1,8 +1,9 @@
|
||||
package net.corda.testing.node.internal
|
||||
|
||||
import net.corda.core.internal.div
|
||||
import java.io.File
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.Path
|
||||
import kotlin.io.path.div
|
||||
|
||||
object ProcessUtilities {
|
||||
@Suppress("LongParameterList")
|
||||
@ -38,19 +39,16 @@ object ProcessUtilities {
|
||||
maximumHeapSize: String? = null,
|
||||
identifier: String = "",
|
||||
environmentVariables: Map<String,String> = emptyMap(),
|
||||
inheritIO: Boolean = true
|
||||
): Process {
|
||||
val command = mutableListOf<String>().apply {
|
||||
add(javaPath)
|
||||
(jdwpPort != null) && add("-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=$jdwpPort")
|
||||
if (maximumHeapSize != null) add("-Xmx$maximumHeapSize")
|
||||
add("-XX:+UseG1GC")
|
||||
addAll(extraJvmArguments)
|
||||
add(className)
|
||||
addAll(arguments)
|
||||
}
|
||||
return ProcessBuilder(command).apply {
|
||||
if (inheritIO) inheritIO()
|
||||
environment().putAll(environmentVariables)
|
||||
environment()["CLASSPATH"] = classPath.joinToString(File.pathSeparator)
|
||||
if (workingDirectory != null) {
|
||||
@ -63,7 +61,7 @@ object ProcessUtilities {
|
||||
}.start()
|
||||
}
|
||||
|
||||
private val javaPath = (System.getProperty("java.home") / "bin" / "java").toString()
|
||||
private val javaPath = Path(System.getProperty("java.home"), "bin", "java").toString()
|
||||
|
||||
val defaultClassPath: List<String> = System.getProperty("java.class.path").split(File.pathSeparator)
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,6 @@ import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.concurrent.doneFuture
|
||||
import net.corda.core.internal.concurrent.fork
|
||||
import net.corda.core.internal.concurrent.map
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.internal.uncheckedCast
|
||||
import net.corda.core.messaging.RPCOps
|
||||
import net.corda.core.node.NetworkParameters
|
||||
@ -59,6 +58,7 @@ import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import java.time.Duration
|
||||
import java.util.*
|
||||
import kotlin.io.path.div
|
||||
import net.corda.nodeapi.internal.config.User as InternalUser
|
||||
|
||||
inline fun <reified I : RPCOps> RPCDriverDSL.startInVmRpcClient(
|
||||
@ -188,23 +188,23 @@ data class RPCDriverDSL(
|
||||
private val driverDSL: DriverDSLImpl, private val externalTrace: Trace?
|
||||
) : InternalDriverDSL by driverDSL {
|
||||
private companion object {
|
||||
const val notificationAddress = "notifications"
|
||||
const val NOTIFICATION_ADDRESS = "notifications"
|
||||
|
||||
private fun ConfigurationImpl.configureCommonSettings(maxFileSize: Int, maxBufferedBytesPerClient: Long) {
|
||||
name = "RPCDriver"
|
||||
managementNotificationAddress = SimpleString(notificationAddress)
|
||||
managementNotificationAddress = SimpleString.of(NOTIFICATION_ADDRESS)
|
||||
isPopulateValidatedUser = true
|
||||
journalBufferSize_NIO = maxFileSize
|
||||
journalBufferSize_AIO = maxFileSize
|
||||
journalFileSize = maxFileSize
|
||||
queueConfigs = listOf(
|
||||
QueueConfiguration(RPCApi.RPC_SERVER_QUEUE_NAME).setAddress(RPCApi.RPC_SERVER_QUEUE_NAME).setDurable(false),
|
||||
QueueConfiguration(RPCApi.RPC_CLIENT_BINDING_REMOVALS).setAddress(notificationAddress)
|
||||
QueueConfiguration.of(RPCApi.RPC_SERVER_QUEUE_NAME).setAddress(RPCApi.RPC_SERVER_QUEUE_NAME).setDurable(false),
|
||||
QueueConfiguration.of(RPCApi.RPC_CLIENT_BINDING_REMOVALS).setAddress(NOTIFICATION_ADDRESS)
|
||||
.setFilterString(RPCApi.RPC_CLIENT_BINDING_REMOVAL_FILTER_EXPRESSION).setDurable(false),
|
||||
QueueConfiguration(RPCApi.RPC_CLIENT_BINDING_ADDITIONS).setAddress(notificationAddress)
|
||||
QueueConfiguration.of(RPCApi.RPC_CLIENT_BINDING_ADDITIONS).setAddress(NOTIFICATION_ADDRESS)
|
||||
.setFilterString(RPCApi.RPC_CLIENT_BINDING_ADDITION_FILTER_EXPRESSION).setDurable(false)
|
||||
)
|
||||
addressesSettings = mapOf(
|
||||
addressSettings = mapOf(
|
||||
"${RPCApi.RPC_CLIENT_QUEUE_NAME_PREFIX}.#" to AddressSettings().apply {
|
||||
maxSizeBytes = maxBufferedBytesPerClient
|
||||
addressFullMessagePolicy = AddressFullMessagePolicy.PAGE
|
||||
|
@ -1,7 +1,9 @@
|
||||
package net.corda.testing.node.internal
|
||||
|
||||
import io.github.classgraph.ClassGraph
|
||||
import net.corda.core.internal.*
|
||||
import net.corda.core.internal.attributes
|
||||
import net.corda.core.internal.mapToSet
|
||||
import net.corda.core.internal.pooledScan
|
||||
import net.corda.core.utilities.contextLogger
|
||||
import net.corda.testing.node.TestCordapp
|
||||
import org.gradle.tooling.GradleConnector
|
||||
@ -9,9 +11,10 @@ import org.gradle.tooling.ProgressEvent
|
||||
import java.io.File
|
||||
import java.io.RandomAccessFile
|
||||
import java.nio.file.Path
|
||||
import java.util.*
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import kotlin.streams.toList
|
||||
import kotlin.io.path.div
|
||||
import kotlin.io.path.exists
|
||||
import kotlin.io.path.useDirectoryEntries
|
||||
|
||||
/**
|
||||
* Implementation of the public [TestCordapp] API.
|
||||
@ -21,39 +24,43 @@ import kotlin.streams.toList
|
||||
* the [scanPackage] may reference a gradle CorDapp project on the local system. In this scenerio the project's "jar" task is executed to
|
||||
* build the CorDapp jar. This allows us to inherit the CorDapp's MANIFEST information without having to do any extra processing.
|
||||
*/
|
||||
data class TestCordappImpl(val scanPackage: String, override val config: Map<String, Any>) : TestCordappInternal() {
|
||||
override fun withConfig(config: Map<String, Any>): TestCordappImpl = copy(config = config)
|
||||
data class ScanPackageTestCordapp(val scanPackage: String,
|
||||
override val config: Map<String, Any> = emptyMap(),
|
||||
val signed: Boolean = false) : TestCordappInternal() {
|
||||
override fun withConfig(config: Map<String, Any>): ScanPackageTestCordapp = copy(config = config)
|
||||
|
||||
override fun withOnlyJarContents(): TestCordappImpl = copy(config = emptyMap())
|
||||
override fun asSigned(): TestCordapp = copy(signed = true)
|
||||
|
||||
override val jarFile: Path
|
||||
get() {
|
||||
val jars = findJars(scanPackage)
|
||||
when (jars.size) {
|
||||
0 -> throw IllegalArgumentException("There are no CorDapps containing the package $scanPackage on the classpath. Make sure " +
|
||||
"the package name is correct and that the CorDapp is added as a gradle dependency.")
|
||||
1 -> return jars.first()
|
||||
else -> throw IllegalArgumentException("There is more than one CorDapp containing the package $scanPackage on the classpath " +
|
||||
"$jars. Specify a package name which is unique to the CorDapp.")
|
||||
}
|
||||
override fun withOnlyJarContents(): ScanPackageTestCordapp = copy(config = emptyMap(), signed = false)
|
||||
|
||||
override val jarFile: Path by lazy {
|
||||
val jars = findJars()
|
||||
val jar = when (jars.size) {
|
||||
0 -> throw IllegalArgumentException("There are no CorDapps containing the package $scanPackage on the classpath. Make sure " +
|
||||
"the package name is correct and that the CorDapp is added as a gradle dependency.")
|
||||
1 -> jars.first()
|
||||
else -> throw IllegalArgumentException("There is more than one CorDapp containing the package $scanPackage on the classpath " +
|
||||
"$jars. Specify a package name which is unique to the CorDapp.")
|
||||
}
|
||||
if (signed) TestCordappSigner.signJarCopy(jar) else jar
|
||||
}
|
||||
|
||||
private fun findJars(): Set<Path> {
|
||||
val rootPaths = findRootPaths(scanPackage)
|
||||
return if (rootPaths.all { it.toString().endsWith(".jar") }) {
|
||||
// We don't need to do anything more if all the root paths are jars
|
||||
rootPaths
|
||||
} else {
|
||||
// Otherwise we need to build those paths which are local projects and extract the built jar from them
|
||||
rootPaths.mapToSet { if (it.toString().endsWith(".jar")) it else buildCordappJar(it) }
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val packageToRootPaths = ConcurrentHashMap<String, Set<Path>>()
|
||||
private val projectRootToBuiltJar = ConcurrentHashMap<Path, Path>()
|
||||
private val log = contextLogger()
|
||||
|
||||
fun findJars(scanPackage: String): Set<Path> {
|
||||
val rootPaths = findRootPaths(scanPackage)
|
||||
return if (rootPaths.all { it.toString().endsWith(".jar") }) {
|
||||
// We don't need to do anything more if all the root paths are jars
|
||||
rootPaths
|
||||
} else {
|
||||
// Otherwise we need to build those paths which are local projects and extract the built jar from them
|
||||
rootPaths.mapTo(HashSet()) { if (it.toString().endsWith(".jar")) it else buildCordappJar(it) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun findRootPaths(scanPackage: String): Set<Path> {
|
||||
return packageToRootPaths.computeIfAbsent(scanPackage) {
|
||||
val classGraph = ClassGraph().acceptPaths(scanPackage.replace('.', '/'))
|
||||
@ -87,11 +94,8 @@ data class TestCordappImpl(val scanPackage: String, override val config: Map<Str
|
||||
runGradleBuild(projectRoot)
|
||||
|
||||
val libs = projectRoot / "build" / "libs"
|
||||
val jars = libs.list {
|
||||
it.filter { it.toString().endsWith(".jar") }
|
||||
.filter { !it.toString().endsWith("sources.jar") }
|
||||
.filter { !it.toString().endsWith("javadoc.jar") }
|
||||
.toList()
|
||||
val jars = libs.useDirectoryEntries("*.jar") { jars ->
|
||||
jars.filter { !it.toString().endsWith("sources.jar") && !it.toString().endsWith("javadoc.jar") }.toList()
|
||||
}.sortedBy { it.attributes().creationTime() }
|
||||
checkNotNull(jars.lastOrNull()) { "No jars were built in $libs" }
|
||||
}
|
@ -2,12 +2,13 @@ package net.corda.testing.node.internal
|
||||
|
||||
import com.typesafe.config.ConfigValueFactory
|
||||
import net.corda.core.internal.copyToDirectory
|
||||
import net.corda.core.internal.createDirectories
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.internal.writeText
|
||||
import net.corda.testing.node.TestCordapp
|
||||
import java.nio.file.FileAlreadyExistsException
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.createDirectories
|
||||
import kotlin.io.path.div
|
||||
import kotlin.io.path.name
|
||||
import kotlin.io.path.writeText
|
||||
|
||||
/**
|
||||
* Extends the public [TestCordapp] API with internal extensions for use within the testing framework and for internal testing of the platform.
|
||||
@ -45,7 +46,7 @@ abstract class TestCordappInternal : TestCordapp() {
|
||||
// Ignore if the node already has the same CorDapp jar. This can happen if the node is being restarted.
|
||||
}
|
||||
val configString = ConfigValueFactory.fromMap(cordapp.config).toConfig().root().render()
|
||||
(configDir / "${jar.fileName.toString().removeSuffix(".jar")}.conf").writeText(configString)
|
||||
(configDir / "${jar.name.removeSuffix(".jar")}.conf").writeText(configString)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,45 @@
|
||||
package net.corda.testing.node.internal
|
||||
|
||||
import net.corda.core.internal.deleteRecursively
|
||||
import net.corda.testing.core.internal.JarSignatureTestUtils.containsKey
|
||||
import net.corda.testing.core.internal.JarSignatureTestUtils.generateKey
|
||||
import net.corda.testing.core.internal.JarSignatureTestUtils.signJar
|
||||
import net.corda.testing.core.internal.JarSignatureTestUtils.unsignJar
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.absolutePathString
|
||||
import kotlin.io.path.copyTo
|
||||
import kotlin.io.path.name
|
||||
|
||||
object TestCordappSigner {
|
||||
private val defaultSignerDir = Files.createTempDirectory("testcordapp-signer")
|
||||
|
||||
init {
|
||||
defaultSignerDir.generateKey(alias = "testcordapp")
|
||||
Runtime.getRuntime().addShutdownHook(Thread(defaultSignerDir::deleteRecursively))
|
||||
}
|
||||
|
||||
fun signJarCopy(jar: Path, signerDir: Path? = null, signatureCount: Int = 1, algorithm: String = "RSA"): Path {
|
||||
val copy = Files.createTempFile(jar.name, ".jar")
|
||||
copy.toFile().deleteOnExit()
|
||||
jar.copyTo(copy, overwrite = true)
|
||||
signJar(copy, signerDir, signatureCount, algorithm)
|
||||
return copy
|
||||
}
|
||||
|
||||
fun signJar(jar: Path, signerDir: Path? = null, signatureCount: Int = 1, algorithm: String = "RSA") {
|
||||
jar.unsignJar()
|
||||
val signerDirToUse = signerDir ?: defaultSignerDir
|
||||
for (i in 1 .. signatureCount) {
|
||||
// Note in the jarsigner tool if -sigfile is not specified then the first 8 chars of alias are used as the file
|
||||
// name for the .SF and .DSA files. (See jarsigner doc). So $i below needs to be at beginning so unique files are
|
||||
// created.
|
||||
val alias = "$i-testcordapp-$algorithm"
|
||||
val password = "secret!"
|
||||
if (!signerDirToUse.containsKey(alias, password)) {
|
||||
signerDirToUse.generateKey(alias, password, "O=Test Company Ltd $i,OU=Test,L=London,C=GB", algorithm)
|
||||
}
|
||||
signerDirToUse.signJar(jar.absolutePathString(), alias, password)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package net.corda.testing.node.internal
|
||||
|
||||
import net.corda.core.internal.copyTo
|
||||
import net.corda.core.utilities.Try
|
||||
import net.corda.core.utilities.Try.Failure
|
||||
import net.corda.core.utilities.Try.Success
|
||||
import net.corda.testing.node.TestCordapp
|
||||
import java.net.URI
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.StandardCopyOption.REPLACE_EXISTING
|
||||
import kotlin.io.path.toPath
|
||||
|
||||
data class UriTestCordapp(val uri: URI,
|
||||
override val config: Map<String, Any> = emptyMap(),
|
||||
val signed: Boolean = false) : TestCordappInternal() {
|
||||
override fun withConfig(config: Map<String, Any>): TestCordapp = copy(config = config)
|
||||
|
||||
override fun asSigned(): TestCordapp = copy(signed = true)
|
||||
|
||||
override fun withOnlyJarContents(): TestCordappInternal = copy(config = emptyMap(), signed = false)
|
||||
|
||||
override val jarFile: Path by lazy {
|
||||
val toPathAttempt = Try.on(uri::toPath)
|
||||
when (toPathAttempt) {
|
||||
is Success -> if (signed) TestCordappSigner.signJarCopy(toPathAttempt.value) else toPathAttempt.value
|
||||
is Failure -> {
|
||||
// URI is not a local path, so we copy it to a temp file and use that.
|
||||
val downloaded = Files.createTempFile("test-cordapp-${uri.path.substringAfterLast("/").substringBeforeLast(".jar")}", ".jar")
|
||||
downloaded.toFile().deleteOnExit()
|
||||
uri.toURL().openStream().use { it.copyTo(downloaded, REPLACE_EXISTING) }
|
||||
if (signed) {
|
||||
TestCordappSigner.signJar(downloaded)
|
||||
}
|
||||
downloaded
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -2,6 +2,10 @@
|
||||
|
||||
package net.corda.testing.node.internal.network
|
||||
|
||||
import jakarta.ws.rs.GET
|
||||
import jakarta.ws.rs.Path
|
||||
import jakarta.ws.rs.Produces
|
||||
import jakarta.ws.rs.core.Response
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.internal.CertRole
|
||||
import net.corda.core.internal.toX500Name
|
||||
@ -24,11 +28,11 @@ import org.bouncycastle.asn1.x509.DistributionPointName
|
||||
import org.bouncycastle.asn1.x509.Extension
|
||||
import org.bouncycastle.asn1.x509.GeneralName
|
||||
import org.bouncycastle.asn1.x509.GeneralNames
|
||||
import org.eclipse.jetty.ee10.servlet.ServletContextHandler
|
||||
import org.eclipse.jetty.ee10.servlet.ServletHolder
|
||||
import org.eclipse.jetty.server.Server
|
||||
import org.eclipse.jetty.server.ServerConnector
|
||||
import org.eclipse.jetty.server.handler.HandlerCollection
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler
|
||||
import org.eclipse.jetty.servlet.ServletHolder
|
||||
import org.eclipse.jetty.server.handler.ContextHandlerCollection
|
||||
import org.glassfish.jersey.server.ResourceConfig
|
||||
import org.glassfish.jersey.servlet.ServletContainer
|
||||
import java.io.Closeable
|
||||
@ -40,10 +44,6 @@ import java.security.cert.X509Certificate
|
||||
import java.time.Duration
|
||||
import java.util.*
|
||||
import javax.security.auth.x500.X500Principal
|
||||
import javax.ws.rs.GET
|
||||
import javax.ws.rs.Path
|
||||
import javax.ws.rs.Produces
|
||||
import javax.ws.rs.core.Response
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
class CrlServer(hostAndPort: NetworkHostAndPort) : Closeable {
|
||||
@ -79,7 +79,7 @@ class CrlServer(hostAndPort: NetworkHostAndPort) : Closeable {
|
||||
}
|
||||
|
||||
private val server: Server = Server(InetSocketAddress(hostAndPort.host, hostAndPort.port)).apply {
|
||||
handler = HandlerCollection().apply {
|
||||
handler = ContextHandlerCollection().apply {
|
||||
addHandler(buildServletContextHandler())
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,15 @@
|
||||
package net.corda.testing.node.internal.network
|
||||
|
||||
import jakarta.ws.rs.Consumes
|
||||
import jakarta.ws.rs.GET
|
||||
import jakarta.ws.rs.POST
|
||||
import jakarta.ws.rs.Path
|
||||
import jakarta.ws.rs.PathParam
|
||||
import jakarta.ws.rs.Produces
|
||||
import jakarta.ws.rs.core.MediaType
|
||||
import jakarta.ws.rs.core.Response
|
||||
import jakarta.ws.rs.core.Response.ok
|
||||
import jakarta.ws.rs.core.Response.status
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.SignedData
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
@ -14,11 +24,11 @@ import net.corda.nodeapi.internal.crypto.CertificateAndKeyPair
|
||||
import net.corda.nodeapi.internal.network.NetworkMap
|
||||
import net.corda.nodeapi.internal.network.ParametersUpdate
|
||||
import net.corda.testing.common.internal.testNetworkParameters
|
||||
import org.eclipse.jetty.ee10.servlet.ServletContextHandler
|
||||
import org.eclipse.jetty.ee10.servlet.ServletHolder
|
||||
import org.eclipse.jetty.server.Server
|
||||
import org.eclipse.jetty.server.ServerConnector
|
||||
import org.eclipse.jetty.server.handler.HandlerCollection
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler
|
||||
import org.eclipse.jetty.servlet.ServletHolder
|
||||
import org.eclipse.jetty.server.handler.ContextHandlerCollection
|
||||
import org.glassfish.jersey.server.ResourceConfig
|
||||
import org.glassfish.jersey.servlet.ServletContainer
|
||||
import java.io.Closeable
|
||||
@ -29,11 +39,6 @@ import java.security.SignatureException
|
||||
import java.time.Duration
|
||||
import java.time.Instant
|
||||
import java.util.*
|
||||
import javax.ws.rs.*
|
||||
import javax.ws.rs.core.MediaType
|
||||
import javax.ws.rs.core.Response
|
||||
import javax.ws.rs.core.Response.ok
|
||||
import javax.ws.rs.core.Response.status
|
||||
|
||||
class NetworkMapServer(private val pollInterval: Duration,
|
||||
hostAndPort: NetworkHostAndPort = NetworkHostAndPort("localhost", 0),
|
||||
@ -54,7 +59,7 @@ class NetworkMapServer(private val pollInterval: Duration,
|
||||
|
||||
init {
|
||||
server = Server(InetSocketAddress(hostAndPort.host, hostAndPort.port)).apply {
|
||||
handler = HandlerCollection().apply {
|
||||
handler = ContextHandlerCollection().apply {
|
||||
addHandler(ServletContextHandler().apply {
|
||||
contextPath = "/"
|
||||
val resourceConfig = ResourceConfig().apply {
|
||||
@ -232,7 +237,7 @@ class NetworkMapServer(private val pollInterval: Duration,
|
||||
null
|
||||
}
|
||||
requireNotNull(requestedParameters)
|
||||
return Response.ok(requestedParameters!!.serialize().bytes).build()
|
||||
return Response.ok(requestedParameters.serialize().bytes).build()
|
||||
}
|
||||
|
||||
@GET
|
||||
|
@ -14,11 +14,12 @@ import net.corda.testing.core.ALICE_NAME
|
||||
import net.corda.testing.core.singleIdentity
|
||||
import net.corda.testing.node.internal.DUMMY_CONTRACTS_CORDAPP
|
||||
import net.corda.testing.node.internal.enclosedCordapp
|
||||
import org.assertj.core.api.Assertions.assertThatExceptionOfType
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import java.security.PublicKey
|
||||
import java.util.*
|
||||
import java.util.Random
|
||||
|
||||
class CustomNotaryTest {
|
||||
private lateinit var mockNet: MockNetwork
|
||||
@ -50,13 +51,15 @@ class CustomNotaryTest {
|
||||
mockNet.stopNodes()
|
||||
}
|
||||
|
||||
@Test(expected = CustomNotaryException::class, timeout=300_000)
|
||||
@Test(timeout=300_000)
|
||||
fun `custom notary service is active`() {
|
||||
val tx = DummyContract.generateInitial(Random().nextInt(), notary, alice.ref(0))
|
||||
val stx = aliceNode.services.signInitialTransaction(tx)
|
||||
val future = aliceNode.startFlow(NotaryFlow.Client(stx))
|
||||
mockNet.runNetwork()
|
||||
future.getOrThrow()
|
||||
assertThatExceptionOfType(CustomNotaryException::class.java).isThrownBy {
|
||||
future.getOrThrow()
|
||||
}
|
||||
}
|
||||
|
||||
class CustomNotaryService(override val services: ServiceHubInternal, override val notaryIdentityKey: PublicKey) : NotaryService() {
|
||||
|
@ -2,13 +2,13 @@ package net.corda.testing.node.internal
|
||||
|
||||
import net.corda.core.internal.cordapp.CordappImpl
|
||||
import net.corda.core.internal.cordapp.get
|
||||
import net.corda.core.internal.inputStream
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TemporaryFolder
|
||||
import java.nio.file.Path
|
||||
import java.util.jar.JarInputStream
|
||||
import kotlin.io.path.inputStream
|
||||
|
||||
class CustomCordappTest {
|
||||
@Rule
|
||||
|
Reference in New Issue
Block a user