mirror of
https://github.com/corda/corda.git
synced 2024-12-19 21:17:58 +00:00
ENT-11065: Remove the need for JVM flags in client code (#7635)
This commit is contained in:
parent
406f7ff292
commit
2e63ca6264
41
build.gradle
41
build.gradle
@ -121,23 +121,6 @@ buildscript {
|
|||||||
ext.fontawesomefx_commons_version = constants.getProperty("fontawesomefxCommonsVersion")
|
ext.fontawesomefx_commons_version = constants.getProperty("fontawesomefxCommonsVersion")
|
||||||
ext.fontawesomefx_fontawesome_version = constants.getProperty("fontawesomefxFontawesomeVersion")
|
ext.fontawesomefx_fontawesome_version = constants.getProperty("fontawesomefxFontawesomeVersion")
|
||||||
ext.javaassist_version = constants.getProperty("javaassistVersion")
|
ext.javaassist_version = constants.getProperty("javaassistVersion")
|
||||||
ext.test_add_opens = [
|
|
||||||
'--add-opens', 'java.base/java.time=ALL-UNNAMED',
|
|
||||||
'--add-opens', 'java.base/java.io=ALL-UNNAMED',
|
|
||||||
'--add-opens', 'java.base/java.util=ALL-UNNAMED',
|
|
||||||
'--add-opens', 'java.base/java.net=ALL-UNNAMED',
|
|
||||||
'--add-opens', 'java.base/java.nio=ALL-UNNAMED',
|
|
||||||
'--add-opens', 'java.base/java.lang.invoke=ALL-UNNAMED',
|
|
||||||
'--add-opens', 'java.base/java.security.cert=ALL-UNNAMED',
|
|
||||||
'--add-opens', 'java.base/java.security=ALL-UNNAMED',
|
|
||||||
'--add-opens', 'java.base/javax.net.ssl=ALL-UNNAMED',
|
|
||||||
'--add-opens', 'java.base/java.lang=ALL-UNNAMED',
|
|
||||||
'--add-opens', 'java.base/java.util.concurrent=ALL-UNNAMED',
|
|
||||||
'--add-opens', 'java.sql/java.sql=ALL-UNNAMED'
|
|
||||||
]
|
|
||||||
ext.test_add_exports = [
|
|
||||||
'--add-exports', 'java.base/sun.nio.ch=ALL-UNNAMED'
|
|
||||||
]
|
|
||||||
|
|
||||||
ext.corda_revision = {
|
ext.corda_revision = {
|
||||||
try {
|
try {
|
||||||
@ -282,11 +265,6 @@ allprojects {
|
|||||||
toolVersion = "0.8.7"
|
toolVersion = "0.8.7"
|
||||||
}
|
}
|
||||||
|
|
||||||
test {
|
|
||||||
jvmArgs test_add_opens
|
|
||||||
jvmArgs test_add_exports
|
|
||||||
}
|
|
||||||
|
|
||||||
java {
|
java {
|
||||||
withSourcesJar()
|
withSourcesJar()
|
||||||
withJavadocJar()
|
withJavadocJar()
|
||||||
@ -330,15 +308,14 @@ allprojects {
|
|||||||
attributes('Corda-Docs-Link': corda_docs_link)
|
attributes('Corda-Docs-Link': corda_docs_link)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType(Test).configureEach {
|
tasks.withType(Test).configureEach {
|
||||||
|
jvmArgs += project(":node:capsule").file("src/main/resources/node-jvm-args.txt").readLines()
|
||||||
|
jvmArgs += "--add-modules=jdk.incubator.foreign" // For the SharedMemoryIncremental
|
||||||
forkEvery = 20
|
forkEvery = 20
|
||||||
ignoreFailures = project.hasProperty('tests.ignoreFailures') ? project.property('tests.ignoreFailures').toBoolean() : false
|
ignoreFailures = project.hasProperty('tests.ignoreFailures') ? project.property('tests.ignoreFailures').toBoolean() : false
|
||||||
failFast = project.hasProperty('tests.failFast') ? project.property('tests.failFast').toBoolean() : false
|
failFast = project.hasProperty('tests.failFast') ? project.property('tests.failFast').toBoolean() : false
|
||||||
|
|
||||||
// Prevent the project from creating temporary files outside of the build directory.
|
|
||||||
systemProperty 'java.io.tmpdir', buildDir.absolutePath
|
|
||||||
|
|
||||||
maxHeapSize = "1g"
|
maxHeapSize = "1g"
|
||||||
|
|
||||||
if (project.path.startsWith(':experimental') && System.getProperty("experimental.test.enable") == null) {
|
if (project.path.startsWith(':experimental') && System.getProperty("experimental.test.enable") == null) {
|
||||||
@ -351,15 +328,15 @@ allprojects {
|
|||||||
// ex.append = false
|
// ex.append = false
|
||||||
}
|
}
|
||||||
|
|
||||||
maxParallelForks = (System.env.CORDA_TESTING_FORKS == null) ? 1 : "$System.env.CORDA_TESTING_FORKS".toInteger()
|
|
||||||
|
|
||||||
systemProperty 'java.security.egd', 'file:/dev/./urandom'
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.withType(Test).configureEach {
|
|
||||||
if (name.contains("integrationTest")) {
|
if (name.contains("integrationTest")) {
|
||||||
maxParallelForks = (System.env.CORDA_INT_TESTING_FORKS == null) ? 1 : "$System.env.CORDA_INT_TESTING_FORKS".toInteger()
|
maxParallelForks = (System.env.CORDA_INT_TESTING_FORKS == null) ? 1 : "$System.env.CORDA_INT_TESTING_FORKS".toInteger()
|
||||||
|
} else {
|
||||||
|
maxParallelForks = (System.env.CORDA_TESTING_FORKS == null) ? 1 : "$System.env.CORDA_TESTING_FORKS".toInteger()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Prevent the project from creating temporary files outside of the build directory.
|
||||||
|
systemProperty 'java.io.tmpdir', buildDir.absolutePath
|
||||||
|
systemProperty 'java.security.egd', 'file:/dev/./urandom'
|
||||||
}
|
}
|
||||||
|
|
||||||
group 'net.corda'
|
group 'net.corda'
|
||||||
|
@ -10,6 +10,9 @@ description 'Corda client RPC modules'
|
|||||||
configurations {
|
configurations {
|
||||||
integrationTestImplementation.extendsFrom testImplementation
|
integrationTestImplementation.extendsFrom testImplementation
|
||||||
integrationTestRuntimeOnly.extendsFrom testRuntimeOnly
|
integrationTestRuntimeOnly.extendsFrom testRuntimeOnly
|
||||||
|
|
||||||
|
smokeTestImplementation.extendsFrom compile
|
||||||
|
smokeTestRuntimeOnly.extendsFrom runtimeOnly
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
@ -28,55 +31,95 @@ sourceSets {
|
|||||||
srcDirs "src/integration-test/resources"
|
srcDirs "src/integration-test/resources"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
smokeTest {
|
||||||
|
kotlin {
|
||||||
|
// We must NOT have any Node code on the classpath, so do NOT
|
||||||
|
// include the test or integrationTest dependencies here.
|
||||||
|
compileClasspath += main.output
|
||||||
|
runtimeClasspath += main.output
|
||||||
|
srcDir file('src/smoke-test/kotlin')
|
||||||
|
}
|
||||||
|
java {
|
||||||
|
compileClasspath += main.output
|
||||||
|
runtimeClasspath += main.output
|
||||||
|
srcDir file('src/smoke-test/java')
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(':core')
|
implementation project(':core')
|
||||||
implementation project(':node-api')
|
implementation project(':node-api')
|
||||||
implementation project(':serialization')
|
implementation project(':serialization')
|
||||||
|
|
||||||
// For caches rather than guava
|
// For caches rather than guava
|
||||||
implementation "com.github.ben-manes.caffeine:caffeine:$caffeine_version"
|
implementation "com.github.ben-manes.caffeine:caffeine:$caffeine_version"
|
||||||
|
|
||||||
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
|
||||||
|
implementation "io.reactivex:rxjava:$rxjava_version"
|
||||||
testImplementation "junit:junit:$junit_version"
|
implementation("org.apache.activemq:artemis-core-client:${artemis_version}") {
|
||||||
|
exclude group: 'org.jgroups', module: 'jgroups'
|
||||||
testRuntimeOnly "org.junit.vintage:junit-vintage-engine:${junit_vintage_version}"
|
}
|
||||||
testRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}"
|
implementation "com.google.guava:guava-testlib:$guava_version"
|
||||||
|
|
||||||
// Unit testing helpers.
|
|
||||||
testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
|
||||||
testImplementation "org.assertj:assertj-core:${assertj_version}"
|
|
||||||
testImplementation "io.dropwizard.metrics:metrics-core:$metrics_version"
|
|
||||||
|
|
||||||
testImplementation project(':node')
|
testImplementation project(':node')
|
||||||
testImplementation project(':node-driver')
|
testImplementation project(':node-driver')
|
||||||
testImplementation project(':client:mock')
|
testImplementation project(':client:mock')
|
||||||
testImplementation project(':core-test-utils')
|
testImplementation project(':core-test-utils')
|
||||||
|
testImplementation "junit:junit:$junit_version"
|
||||||
|
// Unit testing helpers.
|
||||||
|
testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
||||||
|
testImplementation "org.assertj:assertj-core:${assertj_version}"
|
||||||
|
testImplementation "io.dropwizard.metrics:metrics-core:$metrics_version"
|
||||||
|
|
||||||
|
testRuntimeOnly "org.junit.vintage:junit-vintage-engine:${junit_vintage_version}"
|
||||||
|
testRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}"
|
||||||
|
|
||||||
integrationTestImplementation project(path: ':node-api', configuration: 'testArtifacts')
|
integrationTestImplementation project(path: ':node-api', configuration: 'testArtifacts')
|
||||||
integrationTestImplementation project(':common-configuration-parsing')
|
integrationTestImplementation project(':common-configuration-parsing')
|
||||||
integrationTestImplementation project(':finance:contracts')
|
integrationTestImplementation project(':finance:contracts')
|
||||||
integrationTestImplementation project(':finance:workflows')
|
integrationTestImplementation project(':finance:workflows')
|
||||||
integrationTestImplementation project(':test-utils')
|
integrationTestImplementation project(':test-utils')
|
||||||
|
|
||||||
integrationTestImplementation "co.paralleluniverse:quasar-core:$quasar_version"
|
integrationTestImplementation "co.paralleluniverse:quasar-core:$quasar_version"
|
||||||
integrationTestImplementation "org.mockito.kotlin:mockito-kotlin:$mockito_kotlin_version"
|
integrationTestImplementation "org.mockito.kotlin:mockito-kotlin:$mockito_kotlin_version"
|
||||||
|
|
||||||
implementation "io.reactivex:rxjava:$rxjava_version"
|
smokeTestImplementation project(':core')
|
||||||
implementation("org.apache.activemq:artemis-core-client:${artemis_version}") {
|
smokeTestImplementation project(':node-api')
|
||||||
exclude group: 'org.jgroups', module: 'jgroups'
|
smokeTestImplementation project(':finance:contracts')
|
||||||
}
|
smokeTestImplementation project(':finance:workflows')
|
||||||
implementation "com.google.guava:guava-testlib:$guava_version"
|
smokeTestImplementation project(':smoke-test-utils')
|
||||||
|
smokeTestImplementation project(':testing:cordapps:sleeping')
|
||||||
|
smokeTestImplementation "junit:junit:$junit_version"
|
||||||
|
smokeTestImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
||||||
|
smokeTestImplementation "com.google.guava:guava:$guava_version"
|
||||||
|
smokeTestImplementation "org.hamcrest:hamcrest-library:2.1"
|
||||||
|
|
||||||
|
smokeTestRuntimeOnly "org.junit.vintage:junit-vintage-engine:${junit_vintage_version}"
|
||||||
|
smokeTestRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}"
|
||||||
}
|
}
|
||||||
|
|
||||||
task integrationTest(type: Test) {
|
processSmokeTestResources {
|
||||||
|
// Bring in the fully built corda.jar for use by NodeFactory in the smoke tests
|
||||||
|
from(project(":node:capsule").tasks['buildCordaJAR']) {
|
||||||
|
rename 'corda-(.*)', 'corda.jar'
|
||||||
|
}
|
||||||
|
from(project(':finance:workflows').tasks['jar']) {
|
||||||
|
rename '.*finance-workflows-.*', 'cordapp-finance-workflows.jar'
|
||||||
|
}
|
||||||
|
from(project(':finance:contracts').tasks['jar']) {
|
||||||
|
rename '.*finance-contracts-.*', 'cordapp-finance-contracts.jar'
|
||||||
|
}
|
||||||
|
from(project(':testing:cordapps:sleeping').tasks['jar']) {
|
||||||
|
rename 'testing-sleeping-cordapp-*', 'cordapp-sleeping.jar'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register('integrationTest', Test) {
|
||||||
testClassesDirs = sourceSets.integrationTest.output.classesDirs
|
testClassesDirs = sourceSets.integrationTest.output.classesDirs
|
||||||
classpath = sourceSets.integrationTest.runtimeClasspath
|
classpath = sourceSets.integrationTest.runtimeClasspath
|
||||||
|
}
|
||||||
|
|
||||||
jvmArgs test_add_opens
|
tasks.register('smokeTest', Test) {
|
||||||
jvmArgs test_add_exports
|
testClassesDirs = sourceSets.smokeTest.output.classesDirs
|
||||||
|
classpath = sourceSets.smokeTest.runtimeClasspath
|
||||||
}
|
}
|
||||||
|
|
||||||
jar {
|
jar {
|
||||||
|
@ -54,7 +54,7 @@ import org.junit.Test
|
|||||||
import rx.subjects.PublishSubject
|
import rx.subjects.PublishSubject
|
||||||
import java.net.URLClassLoader
|
import java.net.URLClassLoader
|
||||||
import java.nio.file.Paths
|
import java.nio.file.Paths
|
||||||
import java.util.*
|
import java.util.Currency
|
||||||
import java.util.concurrent.CountDownLatch
|
import java.util.concurrent.CountDownLatch
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
import java.util.concurrent.ScheduledExecutorService
|
import java.util.concurrent.ScheduledExecutorService
|
||||||
@ -255,21 +255,12 @@ class CordaRPCClientTest : NodeBasedTest(FINANCE_CORDAPPS, notaries = listOf(DUM
|
|||||||
fun `additional class loader used by WireTransaction when it deserialises its components`() {
|
fun `additional class loader used by WireTransaction when it deserialises its components`() {
|
||||||
val financeLocation = Cash::class.java.location.toPath().toString()
|
val financeLocation = Cash::class.java.location.toPath().toString()
|
||||||
val classPathWithoutFinance = ProcessUtilities.defaultClassPath.filter { financeLocation !in it }
|
val classPathWithoutFinance = ProcessUtilities.defaultClassPath.filter { financeLocation !in it }
|
||||||
val moduleOpens = listOf(
|
|
||||||
"--add-opens", "java.base/java.time=ALL-UNNAMED", "--add-opens", "java.base/java.io=ALL-UNNAMED",
|
|
||||||
"--add-opens", "java.base/java.util=ALL-UNNAMED", "--add-opens", "java.base/java.net=ALL-UNNAMED",
|
|
||||||
"--add-opens", "java.base/java.nio=ALL-UNNAMED", "--add-opens", "java.base/java.lang.invoke=ALL-UNNAMED",
|
|
||||||
"--add-opens", "java.base/java.security.cert=ALL-UNNAMED", "--add-opens", "java.base/javax.net.ssl=ALL-UNNAMED",
|
|
||||||
"--add-opens", "java.base/java.util.concurrent=ALL-UNNAMED", "--add-opens", "java.sql/java.sql=ALL-UNNAMED",
|
|
||||||
"--add-opens", "java.base/java.lang=ALL-UNNAMED"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Create a Cash.State object for the StandaloneCashRpcClient to get
|
// Create a Cash.State object for the StandaloneCashRpcClient to get
|
||||||
node.services.startFlow(CashIssueFlow(100.POUNDS, OpaqueBytes.of(1), identity), InvocationContext.shell()).flatMap { it.resultFuture }.getOrThrow()
|
node.services.startFlow(CashIssueFlow(100.POUNDS, OpaqueBytes.of(1), identity), InvocationContext.shell()).flatMap { it.resultFuture }.getOrThrow()
|
||||||
val outOfProcessRpc = ProcessUtilities.startJavaProcess<StandaloneCashRpcClient>(
|
val outOfProcessRpc = ProcessUtilities.startJavaProcess<StandaloneCashRpcClient>(
|
||||||
classPath = classPathWithoutFinance,
|
classPath = classPathWithoutFinance,
|
||||||
arguments = listOf(node.node.configuration.rpcOptions.address.toString(), financeLocation),
|
arguments = listOf(node.node.configuration.rpcOptions.address.toString(), financeLocation)
|
||||||
extraJvmArguments = moduleOpens
|
|
||||||
)
|
)
|
||||||
assertThat(outOfProcessRpc.waitFor()).isZero() // i.e. no exceptions were thrown
|
assertThat(outOfProcessRpc.waitFor()).isZero() // i.e. no exceptions were thrown
|
||||||
}
|
}
|
||||||
|
@ -89,7 +89,6 @@ class RPCStabilityTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test(timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
@Ignore("TODO JDK17:Fixme")
|
|
||||||
fun `client and server dont leak threads`() {
|
fun `client and server dont leak threads`() {
|
||||||
fun startAndStop() {
|
fun startAndStop() {
|
||||||
rpcDriver {
|
rpcDriver {
|
||||||
@ -122,7 +121,6 @@ class RPCStabilityTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test(timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
@Ignore("TODO JDK17:Fixme")
|
|
||||||
fun `client doesnt leak threads when it fails to start`() {
|
fun `client doesnt leak threads when it fails to start`() {
|
||||||
fun startAndStop() {
|
fun startAndStop() {
|
||||||
rpcDriver {
|
rpcDriver {
|
||||||
@ -491,7 +489,6 @@ class RPCStabilityTests {
|
|||||||
* In this test we create a number of out of process RPC clients that call [TrackSubscriberOps.subscribe] in a loop.
|
* In this test we create a number of out of process RPC clients that call [TrackSubscriberOps.subscribe] in a loop.
|
||||||
*/
|
*/
|
||||||
@Test(timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
@Ignore("TODO JDK17:Fixme")
|
|
||||||
fun `server cleans up queues after disconnected clients`() {
|
fun `server cleans up queues after disconnected clients`() {
|
||||||
rpcDriver {
|
rpcDriver {
|
||||||
val trackSubscriberOpsImpl = object : TrackSubscriberOps {
|
val trackSubscriberOpsImpl = object : TrackSubscriberOps {
|
||||||
@ -547,7 +544,7 @@ class RPCStabilityTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test(timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
@Ignore // TODO: This is ignored because Artemis slow consumers are broken. I'm not deleting it in case we can get the feature fixed.
|
@Ignore // TODO: This is ignored because Artemis slow consumers are broken. I'm not deleting it in case we can get the feature fixed.
|
||||||
fun `slow consumers are kicked`() {
|
fun `slow consumers are kicked`() {
|
||||||
rpcDriver {
|
rpcDriver {
|
||||||
val server = startRpcServer(maxBufferedBytesPerClient = 10 * 1024 * 1024, ops = SlowConsumerRPCOpsImpl()).get()
|
val server = startRpcServer(maxBufferedBytesPerClient = 10 * 1024 * 1024, ops = SlowConsumerRPCOpsImpl()).get()
|
||||||
|
@ -40,7 +40,6 @@ import net.corda.nodeapi.internal.config.User
|
|||||||
import net.corda.sleeping.SleepingFlow
|
import net.corda.sleeping.SleepingFlow
|
||||||
import net.corda.smoketesting.NodeConfig
|
import net.corda.smoketesting.NodeConfig
|
||||||
import net.corda.smoketesting.NodeProcess
|
import net.corda.smoketesting.NodeProcess
|
||||||
import org.apache.commons.io.output.NullOutputStream.NULL_OUTPUT_STREAM
|
|
||||||
import org.hamcrest.text.MatchesPattern
|
import org.hamcrest.text.MatchesPattern
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
@ -50,6 +49,7 @@ import org.junit.Test
|
|||||||
import org.junit.rules.ExpectedException
|
import org.junit.rules.ExpectedException
|
||||||
import java.io.FilterInputStream
|
import java.io.FilterInputStream
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
|
import java.io.OutputStream.nullOutputStream
|
||||||
import java.util.Currency
|
import java.util.Currency
|
||||||
import java.util.concurrent.CountDownLatch
|
import java.util.concurrent.CountDownLatch
|
||||||
import java.util.concurrent.atomic.AtomicInteger
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
@ -67,7 +67,7 @@ class StandaloneCordaRPClientTest {
|
|||||||
val rpcUser = User("rpcUser", "test", permissions = setOf("InvokeRpc.startFlow", "InvokeRpc.killFlow"))
|
val rpcUser = User("rpcUser", "test", permissions = setOf("InvokeRpc.startFlow", "InvokeRpc.killFlow"))
|
||||||
val flowUser = User("flowUser", "test", permissions = setOf("StartFlow.net.corda.finance.flows.CashIssueFlow"))
|
val flowUser = User("flowUser", "test", permissions = setOf("StartFlow.net.corda.finance.flows.CashIssueFlow"))
|
||||||
val port = AtomicInteger(15200)
|
val port = AtomicInteger(15200)
|
||||||
const val attachmentSize = 2116
|
const val ATTACHMENT_SIZE = 2116
|
||||||
val timeout = 60.seconds
|
val timeout = 60.seconds
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,13 +111,13 @@ class StandaloneCordaRPClientTest {
|
|||||||
|
|
||||||
@Test(timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
fun `test attachments`() {
|
fun `test attachments`() {
|
||||||
val attachment = InputStreamAndHash.createInMemoryTestZip(attachmentSize, 1)
|
val attachment = InputStreamAndHash.createInMemoryTestZip(ATTACHMENT_SIZE, 1)
|
||||||
assertFalse(rpcProxy.attachmentExists(attachment.sha256))
|
assertFalse(rpcProxy.attachmentExists(attachment.sha256))
|
||||||
val id = attachment.inputStream.use { rpcProxy.uploadAttachment(it) }
|
val id = attachment.inputStream.use { rpcProxy.uploadAttachment(it) }
|
||||||
assertEquals(attachment.sha256, id, "Attachment has incorrect SHA256 hash")
|
assertEquals(attachment.sha256, id, "Attachment has incorrect SHA256 hash")
|
||||||
|
|
||||||
val hash = HashingInputStream(Hashing.sha256(), rpcProxy.openAttachment(id)).use { it ->
|
val hash = HashingInputStream(Hashing.sha256(), rpcProxy.openAttachment(id)).use {
|
||||||
it.copyTo(NULL_OUTPUT_STREAM)
|
it.copyTo(nullOutputStream())
|
||||||
SecureHash.createSHA256(it.hash().asBytes())
|
SecureHash.createSHA256(it.hash().asBytes())
|
||||||
}
|
}
|
||||||
assertEquals(attachment.sha256, hash)
|
assertEquals(attachment.sha256, hash)
|
||||||
@ -126,13 +126,13 @@ class StandaloneCordaRPClientTest {
|
|||||||
@Ignore("CORDA-1520 - After switching from Kryo to AMQP this test won't work")
|
@Ignore("CORDA-1520 - After switching from Kryo to AMQP this test won't work")
|
||||||
@Test(timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
fun `test wrapped attachments`() {
|
fun `test wrapped attachments`() {
|
||||||
val attachment = InputStreamAndHash.createInMemoryTestZip(attachmentSize, 1)
|
val attachment = InputStreamAndHash.createInMemoryTestZip(ATTACHMENT_SIZE, 1)
|
||||||
assertFalse(rpcProxy.attachmentExists(attachment.sha256))
|
assertFalse(rpcProxy.attachmentExists(attachment.sha256))
|
||||||
val id = WrapperStream(attachment.inputStream).use { rpcProxy.uploadAttachment(it) }
|
val id = WrapperStream(attachment.inputStream).use { rpcProxy.uploadAttachment(it) }
|
||||||
assertEquals(attachment.sha256, id, "Attachment has incorrect SHA256 hash")
|
assertEquals(attachment.sha256, id, "Attachment has incorrect SHA256 hash")
|
||||||
|
|
||||||
val hash = HashingInputStream(Hashing.sha256(), rpcProxy.openAttachment(id)).use { it ->
|
val hash = HashingInputStream(Hashing.sha256(), rpcProxy.openAttachment(id)).use {
|
||||||
it.copyTo(NULL_OUTPUT_STREAM)
|
it.copyTo(nullOutputStream())
|
||||||
SecureHash.createSHA256(it.hash().asBytes())
|
SecureHash.createSHA256(it.hash().asBytes())
|
||||||
}
|
}
|
||||||
assertEquals(attachment.sha256, hash)
|
assertEquals(attachment.sha256, hash)
|
@ -107,6 +107,7 @@ dependencies {
|
|||||||
smokeTestRuntimeOnly "org.junit.vintage:junit-vintage-engine:${junit_vintage_version}"
|
smokeTestRuntimeOnly "org.junit.vintage:junit-vintage-engine:${junit_vintage_version}"
|
||||||
smokeTestRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${junit_jupiter_version}"
|
smokeTestRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${junit_jupiter_version}"
|
||||||
smokeTestRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}"
|
smokeTestRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}"
|
||||||
|
smokeTestRuntimeOnly "org.slf4j:slf4j-simple:$slf4j_version"
|
||||||
|
|
||||||
smokeTestCompile project(':smoke-test-utils')
|
smokeTestCompile project(':smoke-test-utils')
|
||||||
smokeTestCompile "org.assertj:assertj-core:${assertj_version}"
|
smokeTestCompile "org.assertj:assertj-core:${assertj_version}"
|
||||||
@ -143,9 +144,6 @@ task smokeTest(type: Test) {
|
|||||||
dependsOn smokeTestJar
|
dependsOn smokeTestJar
|
||||||
testClassesDirs = sourceSets.smokeTest.output.classesDirs
|
testClassesDirs = sourceSets.smokeTest.output.classesDirs
|
||||||
classpath = sourceSets.smokeTest.runtimeClasspath
|
classpath = sourceSets.smokeTest.runtimeClasspath
|
||||||
|
|
||||||
jvmArgs test_add_opens
|
|
||||||
jvmArgs test_add_exports
|
|
||||||
}
|
}
|
||||||
|
|
||||||
idea {
|
idea {
|
||||||
|
@ -48,7 +48,7 @@ class NodeVersioningTest {
|
|||||||
users = listOf(superUser)
|
users = listOf(superUser)
|
||||||
)
|
)
|
||||||
|
|
||||||
private lateinit var notary: NodeProcess
|
private var notary: NodeProcess? = null
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
@ -57,7 +57,7 @@ class NodeVersioningTest {
|
|||||||
|
|
||||||
@After
|
@After
|
||||||
fun done() {
|
fun done() {
|
||||||
notary.close()
|
notary?.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
|
@ -23,16 +23,18 @@ configurations {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
// These are exposed in our public APIs and are thus "api" dependencies
|
||||||
|
api "org.slf4j:slf4j-api:$slf4j_version"
|
||||||
|
// RxJava: observable streams of events.
|
||||||
|
api "io.reactivex:rxjava:$rxjava_version"
|
||||||
|
|
||||||
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
|
||||||
// SLF4J: commons-logging bindings for a SLF4J back end
|
// SLF4J: commons-logging bindings for a SLF4J back end
|
||||||
implementation "org.slf4j:jcl-over-slf4j:$slf4j_version"
|
implementation "org.slf4j:jcl-over-slf4j:$slf4j_version"
|
||||||
implementation "org.slf4j:slf4j-api:$slf4j_version"
|
|
||||||
// Guava: Google utilities library.
|
// Guava: Google utilities library.
|
||||||
implementation "com.google.guava:guava:$guava_version"
|
implementation "com.google.guava:guava:$guava_version"
|
||||||
// For caches rather than guava
|
// For caches rather than guava
|
||||||
implementation "com.github.ben-manes.caffeine:caffeine:$caffeine_version"
|
implementation "com.github.ben-manes.caffeine:caffeine:$caffeine_version"
|
||||||
// RxJava: observable streams of events.
|
|
||||||
implementation "io.reactivex:rxjava:$rxjava_version"
|
|
||||||
implementation "org.apache.commons:commons-lang3:$commons_lang3_version"
|
implementation "org.apache.commons:commons-lang3:$commons_lang3_version"
|
||||||
// Java ed25519 implementation. See https://github.com/str4d/ed25519-java/
|
// Java ed25519 implementation. See https://github.com/str4d/ed25519-java/
|
||||||
implementation "net.i2p.crypto:eddsa:$eddsa_version"
|
implementation "net.i2p.crypto:eddsa:$eddsa_version"
|
||||||
@ -80,10 +82,6 @@ jar {
|
|||||||
finalizedBy(copyQuasarJar)
|
finalizedBy(copyQuasarJar)
|
||||||
archiveBaseName = 'corda-core'
|
archiveBaseName = 'corda-core'
|
||||||
archiveClassifier = ''
|
archiveClassifier = ''
|
||||||
|
|
||||||
manifest {
|
|
||||||
attributes('Add-Opens': 'java.base/java.net java.base/java.nio')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
processTestResources {
|
processTestResources {
|
||||||
@ -103,6 +101,7 @@ compileTestJava {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test {
|
test {
|
||||||
|
// TODO This obscures whether any Corda client APIs need these JVM flags as well (which they shouldn't do)
|
||||||
jvmArgs += [
|
jvmArgs += [
|
||||||
'--add-exports', 'java.base/sun.security.util=ALL-UNNAMED',
|
'--add-exports', 'java.base/sun.security.util=ALL-UNNAMED',
|
||||||
'--add-exports', 'java.base/sun.security.x509=ALL-UNNAMED'
|
'--add-exports', 'java.base/sun.security.x509=ALL-UNNAMED'
|
||||||
|
@ -166,7 +166,7 @@ fun InputStream.copyTo(target: Path, vararg options: CopyOption): Long = Files.c
|
|||||||
fun InputStream.readFully(): ByteArray = use { it.readBytes() }
|
fun InputStream.readFully(): ByteArray = use { it.readBytes() }
|
||||||
|
|
||||||
/** Calculate the hash of the remaining bytes in this input stream. The stream is closed at the end. */
|
/** Calculate the hash of the remaining bytes in this input stream. The stream is closed at the end. */
|
||||||
fun InputStream.hash(): SecureHash {
|
fun InputStream.hash(): SecureHash.SHA256 {
|
||||||
return use {
|
return use {
|
||||||
val md = MessageDigest.getInstance("SHA-256")
|
val md = MessageDigest.getInstance("SHA-256")
|
||||||
val buffer = ByteArray(DEFAULT_BUFFER_SIZE)
|
val buffer = ByteArray(DEFAULT_BUFFER_SIZE)
|
||||||
@ -309,6 +309,8 @@ inline fun <T, R : Any> Stream<T>.mapNotNull(crossinline transform: (T) -> R?):
|
|||||||
/** Similar to [Collectors.toSet] except the Set is guaranteed to be ordered. */
|
/** Similar to [Collectors.toSet] except the Set is guaranteed to be ordered. */
|
||||||
fun <T> Stream<T>.toSet(): Set<T> = collect(toCollection { LinkedHashSet<T>() })
|
fun <T> Stream<T>.toSet(): Set<T> = collect(toCollection { LinkedHashSet<T>() })
|
||||||
|
|
||||||
|
val Class<*>.isJdkClass: Boolean get() = module.name?.startsWith("java.") == true
|
||||||
|
|
||||||
fun <T> Class<T>.castIfPossible(obj: Any): T? = if (isInstance(obj)) cast(obj) else null
|
fun <T> Class<T>.castIfPossible(obj: Any): T? = if (isInstance(obj)) cast(obj) else null
|
||||||
|
|
||||||
/** Returns a [DeclaredField] wrapper around the declared (possibly non-public) static field of the receiver [Class]. */
|
/** Returns a [DeclaredField] wrapper around the declared (possibly non-public) static field of the receiver [Class]. */
|
||||||
|
@ -88,7 +88,7 @@ inline fun Path.write(createDirs: Boolean = false, vararg options: OpenOption =
|
|||||||
inline fun <reified T : Any> Path.readObject(): T = readBytes().deserialize()
|
inline fun <reified T : Any> Path.readObject(): T = readBytes().deserialize()
|
||||||
|
|
||||||
/** Calculate the hash of the contents of this file. */
|
/** Calculate the hash of the contents of this file. */
|
||||||
inline val Path.hash: SecureHash get() = read { it.hash() }
|
inline val Path.hash: SecureHash.SHA256 get() = read { it.hash() }
|
||||||
|
|
||||||
/* Check if the Path is symbolic link */
|
/* Check if the Path is symbolic link */
|
||||||
fun Path.safeSymbolicRead(): Path = if (isSymbolicLink()) readSymbolicLink() else this
|
fun Path.safeSymbolicRead(): Path = if (isSymbolicLink()) readSymbolicLink() else this
|
||||||
|
@ -93,26 +93,16 @@ class AttachmentsClassLoader(attachments: List<Attachment>,
|
|||||||
* or use a decorator and reflection to bypass the single-call-per-JVM restriction otherwise.
|
* or use a decorator and reflection to bypass the single-call-per-JVM restriction otherwise.
|
||||||
*/
|
*/
|
||||||
private fun setOrDecorateURLStreamHandlerFactory() {
|
private fun setOrDecorateURLStreamHandlerFactory() {
|
||||||
// Retrieve the `URL.factory` field
|
try {
|
||||||
val factoryField = URL::class.java.getDeclaredField("factory")
|
|
||||||
// Make it accessible
|
|
||||||
factoryField.isAccessible = true
|
|
||||||
|
|
||||||
// Check for preset factory, set directly if missing
|
|
||||||
val existingFactory: URLStreamHandlerFactory? = factoryField.get(null) as URLStreamHandlerFactory?
|
|
||||||
if (existingFactory == null) {
|
|
||||||
URL.setURLStreamHandlerFactory(AttachmentURLStreamHandlerFactory)
|
URL.setURLStreamHandlerFactory(AttachmentURLStreamHandlerFactory)
|
||||||
}
|
} catch (e: Error) {
|
||||||
// Otherwise, decorate the existing and replace via reflection
|
|
||||||
// as calling `URL.setURLStreamHandlerFactory` again will throw an error
|
|
||||||
else {
|
|
||||||
log.warn("The URLStreamHandlerFactory was already set in the JVM. Please be aware that this is not recommended.")
|
log.warn("The URLStreamHandlerFactory was already set in the JVM. Please be aware that this is not recommended.")
|
||||||
|
val factoryField = URL::class.java.getDeclaredField("factory").apply { isAccessible = true }
|
||||||
// Retrieve the field "streamHandlerLock" of the class URL that
|
// Retrieve the field "streamHandlerLock" of the class URL that
|
||||||
// is the lock used to synchronize access to the protocol handlers
|
// is the lock used to synchronize access to the protocol handlers
|
||||||
val lockField = URL::class.java.getDeclaredField("streamHandlerLock")
|
val lockField = URL::class.java.getDeclaredField("streamHandlerLock").apply { isAccessible = true }
|
||||||
// It is a private field so we need to make it accessible
|
// It is a private field so we need to make it accessible
|
||||||
// Note: this will only work as-is in JDK8.
|
val existingFactory = factoryField.get(null) as URLStreamHandlerFactory?
|
||||||
lockField.isAccessible = true
|
|
||||||
// Use the same lock to reset the factory
|
// Use the same lock to reset the factory
|
||||||
synchronized(lockField.get(null)) {
|
synchronized(lockField.get(null)) {
|
||||||
// Reset the value to prevent Error due to a factory already defined
|
// Reset the value to prevent Error due to a factory already defined
|
||||||
@ -121,7 +111,7 @@ class AttachmentsClassLoader(attachments: List<Attachment>,
|
|||||||
URL.setURLStreamHandlerFactory { protocol ->
|
URL.setURLStreamHandlerFactory { protocol ->
|
||||||
// route between our own and the pre-existing factory
|
// route between our own and the pre-existing factory
|
||||||
AttachmentURLStreamHandlerFactory.createURLStreamHandler(protocol)
|
AttachmentURLStreamHandlerFactory.createURLStreamHandler(protocol)
|
||||||
?: existingFactory.createURLStreamHandler(protocol)
|
?: existingFactory?.createURLStreamHandler(protocol)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ package net.corda.core.utilities
|
|||||||
|
|
||||||
import net.corda.core.contracts.StateRef
|
import net.corda.core.contracts.StateRef
|
||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.internal.declaredField
|
|
||||||
import org.assertj.core.api.Assertions.catchThrowable
|
import org.assertj.core.api.Assertions.catchThrowable
|
||||||
import org.junit.Assert.assertSame
|
import org.junit.Assert.assertSame
|
||||||
import org.junit.Assert.assertTrue
|
import org.junit.Assert.assertTrue
|
||||||
@ -14,22 +13,17 @@ import kotlin.test.assertEquals
|
|||||||
class ByteArraysTest {
|
class ByteArraysTest {
|
||||||
@Test(timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
fun `slice works`() {
|
fun `slice works`() {
|
||||||
byteArrayOf(9, 9, 0, 1, 2, 3, 4, 9, 9).let {
|
sliceWorksImpl(OpaqueBytesSubSequence(byteArrayOf(9, 9, 0, 1, 2, 3, 4, 9, 9), 2, 5))
|
||||||
sliceWorksImpl(it, OpaqueBytesSubSequence(it, 2, 5))
|
sliceWorksImpl(OpaqueBytes(byteArrayOf(0, 1, 2, 3, 4)))
|
||||||
}
|
|
||||||
byteArrayOf(0, 1, 2, 3, 4).let {
|
|
||||||
sliceWorksImpl(it, OpaqueBytes(it))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun sliceWorksImpl(array: ByteArray, seq: ByteSequence) {
|
private fun sliceWorksImpl(seq: ByteSequence) {
|
||||||
// Python-style negative indices can be implemented later if needed:
|
// Python-style negative indices can be implemented later if needed:
|
||||||
assertSame(IllegalArgumentException::class.java, catchThrowable { seq.slice(-1) }.javaClass)
|
assertSame(IllegalArgumentException::class.java, catchThrowable { seq.slice(-1) }.javaClass)
|
||||||
assertSame(IllegalArgumentException::class.java, catchThrowable { seq.slice(end = -1) }.javaClass)
|
assertSame(IllegalArgumentException::class.java, catchThrowable { seq.slice(end = -1) }.javaClass)
|
||||||
fun check(expected: ByteArray, actual: ByteBuffer) {
|
fun check(expected: ByteArray, actual: ByteBuffer) {
|
||||||
assertEquals(ByteBuffer.wrap(expected), actual)
|
assertEquals(ByteBuffer.wrap(expected), actual)
|
||||||
assertSame(ReadOnlyBufferException::class.java, catchThrowable { actual.array() }.javaClass)
|
assertSame(ReadOnlyBufferException::class.java, catchThrowable { actual.array() }.javaClass)
|
||||||
assertSame(array, actual.declaredField<ByteArray>(ByteBuffer::class, "hb").value)
|
|
||||||
}
|
}
|
||||||
check(byteArrayOf(0, 1, 2, 3, 4), seq.slice())
|
check(byteArrayOf(0, 1, 2, 3, 4), seq.slice())
|
||||||
check(byteArrayOf(0, 1, 2, 3, 4), seq.slice(0, 5))
|
check(byteArrayOf(0, 1, 2, 3, 4), seq.slice(0, 5))
|
||||||
@ -48,14 +42,14 @@ class ByteArraysTest {
|
|||||||
|
|
||||||
@Test(timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
fun `test hex parsing strictly uppercase`() {
|
fun `test hex parsing strictly uppercase`() {
|
||||||
val HEX_REGEX = "^[0-9A-F]+\$".toRegex()
|
val hexRegex = "^[0-9A-F]+\$".toRegex()
|
||||||
|
|
||||||
val privacySalt = net.corda.core.contracts.PrivacySalt()
|
val privacySalt = net.corda.core.contracts.PrivacySalt()
|
||||||
val privacySaltAsHexString = privacySalt.bytes.toHexString()
|
val privacySaltAsHexString = privacySalt.bytes.toHexString()
|
||||||
assertTrue(privacySaltAsHexString.matches(HEX_REGEX))
|
assertTrue(privacySaltAsHexString.matches(hexRegex))
|
||||||
|
|
||||||
val stateRef = StateRef(SecureHash.randomSHA256(), 0)
|
val stateRef = StateRef(SecureHash.randomSHA256(), 0)
|
||||||
val txhashAsHexString = stateRef.txhash.bytes.toHexString()
|
val txhashAsHexString = stateRef.txhash.bytes.toHexString()
|
||||||
assertTrue(txhashAsHexString.matches(HEX_REGEX))
|
assertTrue(txhashAsHexString.matches(hexRegex))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,17 +35,6 @@ shadowJar {
|
|||||||
version = null
|
version = null
|
||||||
zip64 true
|
zip64 true
|
||||||
exclude '**/Log4j2Plugins.dat'
|
exclude '**/Log4j2Plugins.dat'
|
||||||
|
|
||||||
manifest {
|
|
||||||
attributes('Add-Opens': 'java.management/com.sun.jmx.mbeanserver ' +
|
|
||||||
'java.base/java.time java.base/java.io ' +
|
|
||||||
'java.base/java.util java.base/java.net ' +
|
|
||||||
'java.base/java.nio java.base/java.lang.invoke ' +
|
|
||||||
'java.base/java.security.cert java.base/java.security ' +
|
|
||||||
'java.base/javax.net.ssl java.base/java.util.concurrent ' +
|
|
||||||
'java.sql/java.sql'
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ImageVariant {
|
enum ImageVariant {
|
||||||
|
@ -64,9 +64,6 @@ task testJar(type: Jar) {
|
|||||||
task integrationTest(type: Test, dependsOn: []) {
|
task integrationTest(type: Test, dependsOn: []) {
|
||||||
testClassesDirs = sourceSets.integrationTest.output.classesDirs
|
testClassesDirs = sourceSets.integrationTest.output.classesDirs
|
||||||
classpath = sourceSets.integrationTest.runtimeClasspath
|
classpath = sourceSets.integrationTest.runtimeClasspath
|
||||||
|
|
||||||
jvmArgs test_add_opens
|
|
||||||
jvmArgs test_add_exports
|
|
||||||
}
|
}
|
||||||
|
|
||||||
jar {
|
jar {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
kotlin.incremental=true
|
kotlin.incremental=true
|
||||||
org.gradle.jvmargs=-Xmx6g -Dfile.encoding=UTF-8 --add-opens 'java.base/java.time=ALL-UNNAMED' --add-opens 'java.base/java.io=ALL-UNNAMED'
|
org.gradle.jvmargs=-Xmx6g -Dfile.encoding=UTF-8'
|
||||||
org.gradle.caching=false
|
org.gradle.caching=false
|
||||||
owasp.failOnError=false
|
owasp.failOnError=false
|
||||||
owasp.failBuildOnCVSS=11.0
|
owasp.failBuildOnCVSS=11.0
|
||||||
|
@ -104,10 +104,6 @@ artifacts {
|
|||||||
|
|
||||||
jar {
|
jar {
|
||||||
baseName 'corda-node-api'
|
baseName 'corda-node-api'
|
||||||
|
|
||||||
manifest {
|
|
||||||
attributes('Add-Opens': 'java.base/java.io java.base/java.time java.base/java.util java.base/java.lang.invoke java.base/java.security')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
publishing {
|
publishing {
|
||||||
|
@ -17,9 +17,7 @@ import net.corda.core.internal.uncheckedCast
|
|||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import java.lang.reflect.Field
|
|
||||||
import java.lang.reflect.InvocationTargetException
|
import java.lang.reflect.InvocationTargetException
|
||||||
import java.lang.reflect.ParameterizedType
|
|
||||||
import java.net.Proxy
|
import java.net.Proxy
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
@ -28,6 +26,7 @@ import java.time.Duration
|
|||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import java.time.temporal.Temporal
|
import java.time.temporal.Temporal
|
||||||
|
import java.time.temporal.TemporalAmount
|
||||||
import java.util.Properties
|
import java.util.Properties
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import javax.security.auth.x500.X500Principal
|
import javax.security.auth.x500.X500Principal
|
||||||
@ -298,104 +297,45 @@ private fun <T : Enum<T>> enumBridge(clazz: Class<T>, name: String): T {
|
|||||||
*/
|
*/
|
||||||
fun Any.toConfig(): Config = ConfigValueFactory.fromMap(toConfigMap()).toConfig()
|
fun Any.toConfig(): Config = ConfigValueFactory.fromMap(toConfigMap()).toConfig()
|
||||||
|
|
||||||
fun Any?.toConfigValue(): ConfigValue = if (this is ConfigValue) {
|
fun Any?.toConfigValue(): ConfigValue = ConfigValueFactory.fromAnyRef(sanitiseForFromAnyRef(this))
|
||||||
this
|
|
||||||
} else if (this != null) {
|
|
||||||
ConfigValueFactory.fromAnyRef(convertValue(this))
|
|
||||||
} else {
|
|
||||||
ConfigValueFactory.fromAnyRef(null)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
|
|
||||||
// Reflect over the fields of the receiver and generate a value Map that can use to create Config object.
|
// Reflect over the fields of the receiver and generate a value Map that can use to create Config object.
|
||||||
private fun Any.toConfigMap(): Map<String, Any> {
|
private fun Any.toConfigMap(): Map<String, Any?> {
|
||||||
val values = HashMap<String, Any>()
|
val values = LinkedHashMap<String, Any?>()
|
||||||
for (field in javaClass.declaredFields) {
|
for (field in javaClass.declaredFields) {
|
||||||
if (field.isStatic || field.isSynthetic) continue
|
if (field.isStatic || field.isSynthetic) continue
|
||||||
field.isAccessible = true
|
field.isAccessible = true
|
||||||
val value = field.get(this) ?: continue
|
val value = field.get(this) ?: continue
|
||||||
val configValue = if (value is String || value is Boolean || value is Number) {
|
values[field.name] = sanitiseForFromAnyRef(value)
|
||||||
// These types are supported by Config as use as is
|
|
||||||
value
|
|
||||||
} else if (value is Temporal || value is NetworkHostAndPort || value is CordaX500Name ||
|
|
||||||
value is Path || value is URL || value is UUID || value is X500Principal) {
|
|
||||||
// These types make sense to be represented as Strings and the exact inverse parsing function for use in parseAs
|
|
||||||
value.toString()
|
|
||||||
} else if (value is Enum<*>) {
|
|
||||||
// Expicitly use the Enum's name in case the toString is overridden, which would make parsing problematic.
|
|
||||||
value.name
|
|
||||||
} else if (value is Properties) {
|
|
||||||
// For Properties we treat keys with . as nested configs
|
|
||||||
ConfigFactory.parseMap(uncheckedCast(value)).root()
|
|
||||||
} else if (value is Iterable<*>) {
|
|
||||||
value.toConfigIterable(field)
|
|
||||||
} else {
|
|
||||||
// Else this is a custom object recursed over
|
|
||||||
value.toConfigMap()
|
|
||||||
}
|
|
||||||
values[field.name] = configValue
|
|
||||||
}
|
}
|
||||||
return values
|
return values
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun convertValue(value: Any): Any {
|
/**
|
||||||
|
* @see ConfigValueFactory.fromAnyRef
|
||||||
return if (value is String || value is Boolean || value is Number) {
|
*/
|
||||||
|
private fun sanitiseForFromAnyRef(value: Any?): Any? {
|
||||||
|
return when (value) {
|
||||||
// These types are supported by Config as use as is
|
// These types are supported by Config as use as is
|
||||||
value
|
is String, is Boolean, is Number, is ConfigValue, is Duration, null -> value
|
||||||
} else if (value is Temporal || value is NetworkHostAndPort || value is CordaX500Name ||
|
is Enum<*> -> value.name
|
||||||
value is Path || value is URL || value is UUID || value is X500Principal) {
|
// These types make sense to be represented as Strings
|
||||||
// These types make sense to be represented as Strings and the exact inverse parsing function for use in parseAs
|
is Temporal, is TemporalAmount, is NetworkHostAndPort, is CordaX500Name, is Path, is URL, is UUID, is X500Principal -> value.toString()
|
||||||
value.toString()
|
|
||||||
} else if (value is Enum<*>) {
|
|
||||||
// Expicitly use the Enum's name in case the toString is overridden, which would make parsing problematic.
|
|
||||||
value.name
|
|
||||||
} else if (value is Properties) {
|
|
||||||
// For Properties we treat keys with . as nested configs
|
// For Properties we treat keys with . as nested configs
|
||||||
ConfigFactory.parseMap(uncheckedCast(value)).root()
|
is Properties -> ConfigFactory.parseMap(uncheckedCast(value)).root()
|
||||||
} else if (value is Iterable<*>) {
|
is Map<*, *> -> ConfigFactory.parseMap(value.map { it.key.toString() to sanitiseForFromAnyRef(it.value) }.toMap()).root()
|
||||||
value.toConfigIterable()
|
is Iterable<*> -> value.map(::sanitiseForFromAnyRef)
|
||||||
} else {
|
|
||||||
// Else this is a custom object recursed over
|
// Else this is a custom object recursed over
|
||||||
value.toConfigMap()
|
else -> value.toConfigMap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// For Iterables figure out the type parameter and apply the same logic as above on the individual elements.
|
|
||||||
private fun Iterable<*>.toConfigIterable(field: Field): Iterable<Any?> {
|
|
||||||
val elementType = (field.genericType as ParameterizedType).actualTypeArguments[0] as Class<*>
|
|
||||||
return when (elementType) {
|
|
||||||
// For the types already supported by Config we can use the Iterable as is
|
|
||||||
String::class.java -> this
|
|
||||||
Integer::class.java -> this
|
|
||||||
java.lang.Long::class.java -> this
|
|
||||||
java.lang.Double::class.java -> this
|
|
||||||
java.lang.Boolean::class.java -> this
|
|
||||||
LocalDate::class.java -> map(Any?::toString)
|
|
||||||
Instant::class.java -> map(Any?::toString)
|
|
||||||
NetworkHostAndPort::class.java -> map(Any?::toString)
|
|
||||||
Path::class.java -> map(Any?::toString)
|
|
||||||
URL::class.java -> map(Any?::toString)
|
|
||||||
X500Principal::class.java -> map(Any?::toString)
|
|
||||||
UUID::class.java -> map(Any?::toString)
|
|
||||||
CordaX500Name::class.java -> map(Any?::toString)
|
|
||||||
Properties::class.java -> map { ConfigFactory.parseMap(uncheckedCast(it)).root() }
|
|
||||||
else -> if (elementType.isEnum) {
|
|
||||||
map { (it as Enum<*>).name }
|
|
||||||
} else {
|
|
||||||
map { it?.toConfigMap() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun Iterable<*>.toConfigIterable(): Iterable<Any?> = map { element -> element?.let(::convertValue) }
|
|
||||||
|
|
||||||
// The typesafe .getBoolean function is case sensitive, this is a case insensitive version
|
// The typesafe .getBoolean function is case sensitive, this is a case insensitive version
|
||||||
fun Config.getBooleanCaseInsensitive(path: String): Boolean {
|
fun Config.getBooleanCaseInsensitive(path: String): Boolean {
|
||||||
try {
|
try {
|
||||||
return getBoolean(path)
|
return getBoolean(path)
|
||||||
} catch(e:Exception) {
|
} catch (e: Exception) {
|
||||||
val stringVal = getString(path).toLowerCase()
|
val stringVal = getString(path).lowercase()
|
||||||
if (stringVal == "true" || stringVal == "false") {
|
if (stringVal == "true" || stringVal == "false") {
|
||||||
return stringVal.toBoolean()
|
return stringVal.toBoolean()
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,4 @@ data class User(
|
|||||||
val password: String,
|
val password: String,
|
||||||
val permissions: Set<String>) {
|
val permissions: Set<String>) {
|
||||||
override fun toString(): String = "${javaClass.simpleName}($username, permissions=$permissions)"
|
override fun toString(): String = "${javaClass.simpleName}($username, permissions=$permissions)"
|
||||||
@Deprecated("Use toConfig().root().unwrapped() instead", ReplaceWith("toConfig().root().unwrapped()"))
|
|
||||||
fun toMap(): Map<String, Any> = toConfig().root().unwrapped()
|
|
||||||
}
|
}
|
||||||
|
@ -9,25 +9,18 @@ import javax.net.ssl.ManagerFactoryParameters
|
|||||||
import javax.net.ssl.X509ExtendedKeyManager
|
import javax.net.ssl.X509ExtendedKeyManager
|
||||||
import javax.net.ssl.X509KeyManager
|
import javax.net.ssl.X509KeyManager
|
||||||
|
|
||||||
class CertHoldingKeyManagerFactorySpiWrapper(private val factorySpi: KeyManagerFactorySpi, private val amqpConfig: AMQPConfiguration) : KeyManagerFactorySpi() {
|
private class CertHoldingKeyManagerFactorySpiWrapper(private val keyManagerFactory: KeyManagerFactory,
|
||||||
|
private val amqpConfig: AMQPConfiguration) : KeyManagerFactorySpi() {
|
||||||
override fun engineInit(keyStore: KeyStore?, password: CharArray?) {
|
override fun engineInit(keyStore: KeyStore?, password: CharArray?) {
|
||||||
val engineInitMethod = KeyManagerFactorySpi::class.java.getDeclaredMethod("engineInit", KeyStore::class.java, CharArray::class.java)
|
keyManagerFactory.init(keyStore, password)
|
||||||
engineInitMethod.isAccessible = true
|
|
||||||
engineInitMethod.invoke(factorySpi, keyStore, password)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun engineInit(spec: ManagerFactoryParameters?) {
|
override fun engineInit(spec: ManagerFactoryParameters?) {
|
||||||
val engineInitMethod = KeyManagerFactorySpi::class.java.getDeclaredMethod("engineInit", ManagerFactoryParameters::class.java)
|
keyManagerFactory.init(spec)
|
||||||
engineInitMethod.isAccessible = true
|
|
||||||
engineInitMethod.invoke(factorySpi, spec)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getKeyManagersImpl(): Array<KeyManager> {
|
private fun getKeyManagersImpl(): Array<KeyManager> {
|
||||||
val engineGetKeyManagersMethod = KeyManagerFactorySpi::class.java.getDeclaredMethod("engineGetKeyManagers")
|
return keyManagerFactory.keyManagers.map {
|
||||||
engineGetKeyManagersMethod.isAccessible = true
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
val keyManagers = engineGetKeyManagersMethod.invoke(factorySpi) as Array<KeyManager>
|
|
||||||
return if (factorySpi is CertHoldingKeyManagerFactorySpiWrapper) keyManagers else keyManagers.map {
|
|
||||||
val aliasProvidingKeyManager = getDefaultKeyManager(it)
|
val aliasProvidingKeyManager = getDefaultKeyManager(it)
|
||||||
// Use the SNIKeyManager if keystore has several entries and only for clients and non-openSSL servers.
|
// Use the SNIKeyManager if keystore has several entries and only for clients and non-openSSL servers.
|
||||||
// Condition of using SNIKeyManager: if its client, or JDKSsl server.
|
// Condition of using SNIKeyManager: if its client, or JDKSsl server.
|
||||||
@ -62,15 +55,11 @@ class CertHoldingKeyManagerFactorySpiWrapper(private val factorySpi: KeyManagerF
|
|||||||
* the wrapper is not thread safe as in it will return the last used alias/cert chain and has itself no notion
|
* the wrapper is not thread safe as in it will return the last used alias/cert chain and has itself no notion
|
||||||
* of belonging to a certain channel.
|
* of belonging to a certain channel.
|
||||||
*/
|
*/
|
||||||
class CertHoldingKeyManagerFactoryWrapper(factory: KeyManagerFactory, amqpConfig: AMQPConfiguration) : KeyManagerFactory(getFactorySpi(factory, amqpConfig), factory.provider, factory.algorithm) {
|
class CertHoldingKeyManagerFactoryWrapper(factory: KeyManagerFactory, amqpConfig: AMQPConfiguration) : KeyManagerFactory(
|
||||||
companion object {
|
CertHoldingKeyManagerFactorySpiWrapper(factory, amqpConfig),
|
||||||
private fun getFactorySpi(factory: KeyManagerFactory, amqpConfig: AMQPConfiguration): KeyManagerFactorySpi {
|
factory.provider,
|
||||||
val spiField = KeyManagerFactory::class.java.getDeclaredField("factorySpi")
|
factory.algorithm
|
||||||
spiField.isAccessible = true
|
) {
|
||||||
return CertHoldingKeyManagerFactorySpiWrapper(spiField.get(factory) as KeyManagerFactorySpi, amqpConfig)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getCurrentCertChain(): Array<out X509Certificate>? {
|
fun getCurrentCertChain(): Array<out X509Certificate>? {
|
||||||
val keyManager = keyManagers.firstOrNull()
|
val keyManager = keyManagers.firstOrNull()
|
||||||
val alias = if (keyManager is AliasProvidingKeyMangerWrapper) keyManager.lastAlias else null
|
val alias = if (keyManager is AliasProvidingKeyMangerWrapper) keyManager.lastAlias else null
|
||||||
@ -78,4 +67,4 @@ class CertHoldingKeyManagerFactoryWrapper(factory: KeyManagerFactory, amqpConfig
|
|||||||
keyManager.getCertificateChain(alias)
|
keyManager.getCertificateChain(alias)
|
||||||
} else null
|
} else null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,34 +7,24 @@ import javax.net.ssl.TrustManagerFactory
|
|||||||
import javax.net.ssl.TrustManagerFactorySpi
|
import javax.net.ssl.TrustManagerFactorySpi
|
||||||
import javax.net.ssl.X509ExtendedTrustManager
|
import javax.net.ssl.X509ExtendedTrustManager
|
||||||
|
|
||||||
class LoggingTrustManagerFactorySpiWrapper(private val factorySpi: TrustManagerFactorySpi) : TrustManagerFactorySpi() {
|
class LoggingTrustManagerFactorySpiWrapper(private val trustManagerFactory: TrustManagerFactory) : TrustManagerFactorySpi() {
|
||||||
override fun engineGetTrustManagers(): Array<TrustManager> {
|
override fun engineGetTrustManagers(): Array<TrustManager> {
|
||||||
val engineGetTrustManagersMethod = TrustManagerFactorySpi::class.java.getDeclaredMethod("engineGetTrustManagers")
|
return trustManagerFactory.trustManagers
|
||||||
engineGetTrustManagersMethod.isAccessible = true
|
.mapNotNull { (it as? X509ExtendedTrustManager)?.let(::LoggingTrustManagerWrapper) }
|
||||||
@Suppress("UNCHECKED_CAST")
|
.toTypedArray()
|
||||||
val trustManagers = engineGetTrustManagersMethod.invoke(factorySpi) as Array<TrustManager>
|
|
||||||
return if (factorySpi is LoggingTrustManagerFactorySpiWrapper) trustManagers else trustManagers.filterIsInstance(X509ExtendedTrustManager::class.java).map { LoggingTrustManagerWrapper(it) }.toTypedArray()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun engineInit(ks: KeyStore?) {
|
override fun engineInit(ks: KeyStore?) {
|
||||||
val engineInitMethod = TrustManagerFactorySpi::class.java.getDeclaredMethod("engineInit", KeyStore::class.java)
|
trustManagerFactory.init(ks)
|
||||||
engineInitMethod.isAccessible = true
|
|
||||||
engineInitMethod.invoke(factorySpi, ks)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun engineInit(spec: ManagerFactoryParameters?) {
|
override fun engineInit(spec: ManagerFactoryParameters?) {
|
||||||
val engineInitMethod = TrustManagerFactorySpi::class.java.getDeclaredMethod("engineInit", ManagerFactoryParameters::class.java)
|
trustManagerFactory.init(spec)
|
||||||
engineInitMethod.isAccessible = true
|
|
||||||
engineInitMethod.invoke(factorySpi, spec)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class LoggingTrustManagerFactoryWrapper(factory: TrustManagerFactory) : TrustManagerFactory(getFactorySpi(factory), factory.provider, factory.algorithm) {
|
class LoggingTrustManagerFactoryWrapper(factory: TrustManagerFactory) : TrustManagerFactory(
|
||||||
companion object {
|
LoggingTrustManagerFactorySpiWrapper(factory),
|
||||||
private fun getFactorySpi(factory: TrustManagerFactory): TrustManagerFactorySpi {
|
factory.provider,
|
||||||
val spiField = TrustManagerFactory::class.java.getDeclaredField("factorySpi")
|
factory.algorithm
|
||||||
spiField.isAccessible = true
|
)
|
||||||
return LoggingTrustManagerFactorySpiWrapper(spiField.get(factory) as TrustManagerFactorySpi)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -2,6 +2,7 @@ package net.corda.nodeapi.internal.rpc.client
|
|||||||
|
|
||||||
import net.corda.core.concurrent.CordaFuture
|
import net.corda.core.concurrent.CordaFuture
|
||||||
import net.corda.core.toFuture
|
import net.corda.core.toFuture
|
||||||
|
import net.corda.serialization.internal.NotSerializableException
|
||||||
import net.corda.serialization.internal.amqp.CustomSerializer
|
import net.corda.serialization.internal.amqp.CustomSerializer
|
||||||
import net.corda.serialization.internal.amqp.SerializerFactory
|
import net.corda.serialization.internal.amqp.SerializerFactory
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
@ -20,9 +21,7 @@ class RpcClientCordaFutureSerializer (factory: SerializerFactory)
|
|||||||
try {
|
try {
|
||||||
return proxy.observable.toFuture()
|
return proxy.observable.toFuture()
|
||||||
} catch (e: NotSerializableException) {
|
} catch (e: NotSerializableException) {
|
||||||
throw NotSerializableException("Failed to deserialize Future from proxy Observable - ${e.message}\n").apply {
|
throw NotSerializableException("Failed to deserialize Future from proxy Observable - ${e.message}\n", e.cause)
|
||||||
initCause(e.cause)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
package net.corda.nodeapi.internal.serialization.kryo
|
package net.corda.nodeapi.internal.serialization.kryo
|
||||||
|
|
||||||
import net.corda.core.internal.declaredField
|
|
||||||
import net.corda.serialization.internal.ByteBufferOutputStream
|
import net.corda.serialization.internal.ByteBufferOutputStream
|
||||||
import org.assertj.core.api.Assertions.catchThrowable
|
import org.assertj.core.api.Assertions.catchThrowable
|
||||||
import org.junit.Assert.assertArrayEquals
|
import org.junit.Assert.assertArrayEquals
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.io.*
|
import java.io.InputStream
|
||||||
|
import java.io.OutputStream
|
||||||
import java.nio.BufferOverflowException
|
import java.nio.BufferOverflowException
|
||||||
import java.util.*
|
import java.util.Random
|
||||||
import java.util.zip.DeflaterOutputStream
|
import java.util.zip.DeflaterOutputStream
|
||||||
import java.util.zip.InflaterInputStream
|
import java.util.zip.InflaterInputStream
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
@ -67,15 +67,12 @@ class KryoStreamsTest {
|
|||||||
fun `ByteBufferOutputStream works`() {
|
fun `ByteBufferOutputStream works`() {
|
||||||
val stream = ByteBufferOutputStream(3)
|
val stream = ByteBufferOutputStream(3)
|
||||||
stream.write("abc".toByteArray())
|
stream.write("abc".toByteArray())
|
||||||
val getBuf = stream.declaredField<ByteArray>(ByteArrayOutputStream::class, "buf")::value
|
|
||||||
assertEquals(3, getBuf().size)
|
|
||||||
repeat(2) {
|
repeat(2) {
|
||||||
assertSame<Any>(BufferOverflowException::class.java, catchThrowable {
|
assertSame<Any>(BufferOverflowException::class.java, catchThrowable {
|
||||||
stream.alsoAsByteBuffer(9) {
|
stream.alsoAsByteBuffer(9) {
|
||||||
it.put("0123456789".toByteArray())
|
it.put("0123456789".toByteArray())
|
||||||
}
|
}
|
||||||
}.javaClass)
|
}.javaClass)
|
||||||
assertEquals(3 + 9, getBuf().size)
|
|
||||||
}
|
}
|
||||||
// This time make too much space:
|
// This time make too much space:
|
||||||
stream.alsoAsByteBuffer(11) {
|
stream.alsoAsByteBuffer(11) {
|
||||||
|
@ -280,10 +280,6 @@ tasks.register('integrationTest', Test) {
|
|||||||
testClassesDirs = sourceSets.integrationTest.output.classesDirs
|
testClassesDirs = sourceSets.integrationTest.output.classesDirs
|
||||||
classpath = sourceSets.integrationTest.runtimeClasspath
|
classpath = sourceSets.integrationTest.runtimeClasspath
|
||||||
maxParallelForks = (System.env.CORDA_NODE_INT_TESTING_FORKS == null) ? 1 : "$System.env.CORDA_NODE_INT_TESTING_FORKS".toInteger()
|
maxParallelForks = (System.env.CORDA_NODE_INT_TESTING_FORKS == null) ? 1 : "$System.env.CORDA_NODE_INT_TESTING_FORKS".toInteger()
|
||||||
|
|
||||||
jvmArgs test_add_opens
|
|
||||||
jvmArgs test_add_exports
|
|
||||||
|
|
||||||
// CertificateRevocationListNodeTests
|
// CertificateRevocationListNodeTests
|
||||||
systemProperty 'net.corda.dpcrl.connect.timeout', '4000'
|
systemProperty 'net.corda.dpcrl.connect.timeout', '4000'
|
||||||
}
|
}
|
||||||
@ -292,9 +288,6 @@ tasks.register('slowIntegrationTest', Test) {
|
|||||||
testClassesDirs = sourceSets.slowIntegrationTest.output.classesDirs
|
testClassesDirs = sourceSets.slowIntegrationTest.output.classesDirs
|
||||||
classpath = sourceSets.slowIntegrationTest.runtimeClasspath
|
classpath = sourceSets.slowIntegrationTest.runtimeClasspath
|
||||||
maxParallelForks = 1
|
maxParallelForks = 1
|
||||||
|
|
||||||
jvmArgs test_add_opens
|
|
||||||
jvmArgs test_add_exports
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// quasar exclusions upon agent code instrumentation at run-time
|
// quasar exclusions upon agent code instrumentation at run-time
|
||||||
@ -332,9 +325,6 @@ quasar {
|
|||||||
|
|
||||||
jar {
|
jar {
|
||||||
baseName 'corda-node'
|
baseName 'corda-node'
|
||||||
manifest {
|
|
||||||
attributes('Add-Opens': 'java.base/java.time java.base/java.io java.base/java.util java.base/java.net')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.named('test', Test) {
|
tasks.named('test', Test) {
|
||||||
|
@ -57,15 +57,15 @@ tasks.register('buildCordaJAR', FatCapsule) {
|
|||||||
with jar
|
with jar
|
||||||
|
|
||||||
manifest {
|
manifest {
|
||||||
attributes('Add-Opens': 'java.management/com.sun.jmx.mbeanserver java.base/java.net java.base/java.lang java.base/java.time')
|
attributes('Add-Opens': 'java.management/com.sun.jmx.mbeanserver')
|
||||||
}
|
}
|
||||||
|
|
||||||
capsuleManifest {
|
capsuleManifest {
|
||||||
applicationVersion = corda_release_version
|
applicationVersion = corda_release_version
|
||||||
applicationId = "net.corda.node.Corda"
|
applicationId = "net.corda.node.Corda"
|
||||||
// See experimental/quasar-hook/README.md for how to generate.
|
// See experimental/quasar-hook/README.md for how to generate.
|
||||||
def quasarExcludeExpression = "x(antlr**;bftsmart**;co.paralleluniverse**;com.codahale**;com.esotericsoftware**;com.fasterxml**;com.google**;com.ibm**;com.intellij**;com.jcabi**;org.mockito**;com.opengamma**;com.typesafe**;com.zaxxer**;de.javakaffee**;groovy**;groovyjarjarantlr**;groovyjarjarasm**;io.atomix**;io.github**;io.netty**;jdk**;kotlin**;net.corda.djvm**;djvm**;net.bytebuddy**;net.i2p**;org.apache**;org.bouncycastle**;org.codehaus**;org.crsh**;org.dom4j**;org.fusesource**;org.h2**;org.hibernate**;org.jboss**;org.jcp**;org.joda**;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**;io.opentelemetry**)"
|
def quasarExcludeExpression = "x(antlr**;bftsmart**;co.paralleluniverse**;com.codahale**;com.esotericsoftware**;com.fasterxml**;com.google**;com.ibm**;com.intellij**;com.jcabi**;org.mockito**;com.opengamma**;com.typesafe**;com.zaxxer**;de.javakaffee**;groovy**;groovyjarjarantlr**;groovyjarjarasm**;io.atomix**;io.github**;io.netty**;jdk**;kotlin**;net.bytebuddy**;net.i2p**;org.apache**;org.bouncycastle**;org.codehaus**;org.crsh**;org.dom4j**;org.fusesource**;org.h2**;org.hibernate**;org.jboss**;org.jcp**;org.joda**;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**;io.opentelemetry**)"
|
||||||
def quasarClassLoaderExclusion = "l(net.corda.djvm.**;net.corda.core.serialization.internal.**)"
|
def quasarClassLoaderExclusion = "l(net.corda.core.serialization.internal.**)"
|
||||||
def quasarOptions = "m"
|
def quasarOptions = "m"
|
||||||
javaAgents = quasar_classifier ? ["quasar-core-${quasar_version}-${quasar_classifier}.jar=${quasarOptions}${quasarExcludeExpression}${quasarClassLoaderExclusion}"] : ["quasar-core-${quasar_version}.jar=${quasarExcludeExpression}${quasarClassLoaderExclusion}"]
|
javaAgents = quasar_classifier ? ["quasar-core-${quasar_version}-${quasar_classifier}.jar=${quasarOptions}${quasarExcludeExpression}${quasarClassLoaderExclusion}"] : ["quasar-core-${quasar_version}.jar=${quasarExcludeExpression}${quasarClassLoaderExclusion}"]
|
||||||
systemProperties['visualvm.display.name'] = 'Corda'
|
systemProperties['visualvm.display.name'] = 'Corda'
|
||||||
@ -73,7 +73,6 @@ tasks.register('buildCordaJAR', FatCapsule) {
|
|||||||
|
|
||||||
// JVM configuration:
|
// JVM configuration:
|
||||||
// - Constrain to small heap sizes to ease development on low end devices.
|
// - Constrain to small heap sizes to ease development on low end devices.
|
||||||
// - Switch to the G1 GC which is going to be the default in Java 9 and gives low pause times/string dedup.
|
|
||||||
// NOTE: these can be overridden in node.conf.
|
// NOTE: these can be overridden in node.conf.
|
||||||
//
|
//
|
||||||
// If you change these flags, please also update Driver.kt
|
// If you change these flags, please also update Driver.kt
|
||||||
|
@ -2,23 +2,32 @@
|
|||||||
// must also be in the default package. When using Kotlin there are a whole host of exceptions
|
// must also be in the default package. When using Kotlin there are a whole host of exceptions
|
||||||
// trying to construct this from Capsule, so it is written in Java.
|
// trying to construct this from Capsule, so it is written in Java.
|
||||||
|
|
||||||
import com.typesafe.config.*;
|
import com.typesafe.config.Config;
|
||||||
|
import com.typesafe.config.ConfigException;
|
||||||
|
import com.typesafe.config.ConfigFactory;
|
||||||
|
import com.typesafe.config.ConfigParseOptions;
|
||||||
|
import com.typesafe.config.ConfigValue;
|
||||||
import sun.misc.Signal;
|
import sun.misc.Signal;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URL;
|
import java.io.InputStream;
|
||||||
import java.nio.file.DirectoryStream;
|
import java.io.InputStreamReader;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.*;
|
import java.util.ArrayList;
|
||||||
import java.util.jar.JarInputStream;
|
import java.util.Arrays;
|
||||||
import java.util.jar.Manifest;
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import static com.typesafe.config.ConfigUtil.splitPath;
|
import static com.typesafe.config.ConfigUtil.splitPath;
|
||||||
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
|
import static java.util.Objects.requireNonNull;
|
||||||
|
import static java.util.stream.Collectors.toList;
|
||||||
import static java.util.stream.Collectors.toMap;
|
import static java.util.stream.Collectors.toMap;
|
||||||
|
|
||||||
public class CordaCaplet extends Capsule {
|
public class CordaCaplet extends Capsule {
|
||||||
@ -47,7 +56,7 @@ public class CordaCaplet extends Capsule {
|
|||||||
|
|
||||||
File getConfigFile(List<String> args, String baseDir) {
|
File getConfigFile(List<String> args, String baseDir) {
|
||||||
String config = getOptionMultiple(args, Arrays.asList("--config-file", "-f"));
|
String config = getOptionMultiple(args, Arrays.asList("--config-file", "-f"));
|
||||||
return (config == null || config.equals("")) ? new File(baseDir, "node.conf") : new File(config);
|
return (config == null || config.isEmpty()) ? new File(baseDir, "node.conf") : new File(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
String getBaseDirectory(List<String> args) {
|
String getBaseDirectory(List<String> args) {
|
||||||
@ -77,7 +86,7 @@ public class CordaCaplet extends Capsule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (arg.toLowerCase().startsWith(lowerCaseOption)) {
|
if (arg.toLowerCase().startsWith(lowerCaseOption)) {
|
||||||
if (arg.length() > option.length() && arg.substring(option.length(), option.length() + 1).equals("=")) {
|
if (arg.length() > option.length() && arg.charAt(option.length()) == '=') {
|
||||||
return arg.substring(option.length() + 1);
|
return arg.substring(option.length() + 1);
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
@ -109,26 +118,18 @@ public class CordaCaplet extends Capsule {
|
|||||||
// For multiple instances Capsule jvm args handling works on basis that one overrides the other.
|
// For multiple instances Capsule jvm args handling works on basis that one overrides the other.
|
||||||
@Override
|
@Override
|
||||||
protected int launch(ProcessBuilder pb) throws IOException, InterruptedException {
|
protected int launch(ProcessBuilder pb) throws IOException, InterruptedException {
|
||||||
if (isAtLeastJavaVersion11()) {
|
List<String> args = pb.command();
|
||||||
List<String> args = pb.command();
|
args.addAll(1, getNodeJvmArgs());
|
||||||
List<String> myArgs = Arrays.asList(
|
pb.command(args);
|
||||||
"--add-opens=java.management/com.sun.jmx.mbeanserver=ALL-UNNAMED",
|
|
||||||
"--add-opens=java.base/java.lang=ALL-UNNAMED",
|
|
||||||
"--add-opens=java.base/java.lang.reflect=ALL-UNNAMED",
|
|
||||||
"--add-opens=java.base/java.lang.invoke=ALL-UNNAMED",
|
|
||||||
"--add-opens=java.base/java.util=ALL-UNNAMED",
|
|
||||||
"--add-opens=java.base/java.time=ALL-UNNAMED",
|
|
||||||
"--add-opens=java.base/java.io=ALL-UNNAMED",
|
|
||||||
"--add-opens=java.base/java.net=ALL-UNNAMED",
|
|
||||||
"--add-opens=java.base/javax.net.ssl=ALL-UNNAMED",
|
|
||||||
"--add-opens=java.base/java.security.cert=ALL-UNNAMED",
|
|
||||||
"--add-opens=java.base/java.nio=ALL-UNNAMED");
|
|
||||||
args.addAll(1, myArgs);
|
|
||||||
pb.command(args);
|
|
||||||
}
|
|
||||||
return super.launch(pb);
|
return super.launch(pb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<String> getNodeJvmArgs() throws IOException {
|
||||||
|
try (InputStream resource = requireNonNull(getClass().getResourceAsStream("/node-jvm-args.txt"))) {
|
||||||
|
return new BufferedReader(new InputStreamReader(resource)).lines().collect(toList());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Overriding the Caplet classpath generation via the intended interface in Capsule.
|
* Overriding the Caplet classpath generation via the intended interface in Capsule.
|
||||||
*/
|
*/
|
||||||
@ -148,12 +149,12 @@ public class CordaCaplet extends Capsule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add additional directories of JARs to the classpath (at the end), e.g., for JDBC drivers.
|
// Add additional directories of JARs to the classpath (at the end), e.g., for JDBC drivers.
|
||||||
augmentClasspath((List<Path>) cp, new File(baseDir, "drivers"));
|
augmentClasspath((List<Path>) cp, Path.of(baseDir, "drivers"));
|
||||||
try {
|
try {
|
||||||
List<String> jarDirs = nodeConfig.getStringList("jarDirs");
|
List<String> jarDirs = nodeConfig.getStringList("jarDirs");
|
||||||
log(LOG_VERBOSE, "Configured JAR directories = " + jarDirs);
|
log(LOG_VERBOSE, "Configured JAR directories = " + jarDirs);
|
||||||
for (String jarDir : jarDirs) {
|
for (String jarDir : jarDirs) {
|
||||||
augmentClasspath((List<Path>) cp, new File(jarDir));
|
augmentClasspath((List<Path>) cp, Path.of(jarDir));
|
||||||
}
|
}
|
||||||
} catch (ConfigException.Missing e) {
|
} catch (ConfigException.Missing e) {
|
||||||
// Ignore since it's ok to be Missing. Other errors would be unexpected.
|
// Ignore since it's ok to be Missing. Other errors would be unexpected.
|
||||||
@ -183,9 +184,6 @@ public class CordaCaplet extends Capsule {
|
|||||||
jvmArgs.add("-XX:+HeapDumpOnOutOfMemoryError");
|
jvmArgs.add("-XX:+HeapDumpOnOutOfMemoryError");
|
||||||
jvmArgs.add("-XX:+CrashOnOutOfMemoryError");
|
jvmArgs.add("-XX:+CrashOnOutOfMemoryError");
|
||||||
}
|
}
|
||||||
if (isAtLeastJavaVersion11()) {
|
|
||||||
jvmArgs.add("-Dnashorn.args=--no-deprecation-warning");
|
|
||||||
}
|
|
||||||
return (T) jvmArgs;
|
return (T) jvmArgs;
|
||||||
} else if (ATTR_SYSTEM_PROPERTIES == attr) {
|
} else if (ATTR_SYSTEM_PROPERTIES == attr) {
|
||||||
// Add system properties, if specified, from the config.
|
// Add system properties, if specified, from the config.
|
||||||
@ -193,7 +191,7 @@ public class CordaCaplet extends Capsule {
|
|||||||
try {
|
try {
|
||||||
Map<String, ?> overrideSystemProps = nodeConfig.getConfig("systemProperties").entrySet().stream()
|
Map<String, ?> overrideSystemProps = nodeConfig.getConfig("systemProperties").entrySet().stream()
|
||||||
.map(Property::create)
|
.map(Property::create)
|
||||||
.collect(toMap(Property::getKey, Property::getValue));
|
.collect(toMap(Property::key, Property::value));
|
||||||
log(LOG_VERBOSE, "Configured system properties = " + overrideSystemProps);
|
log(LOG_VERBOSE, "Configured system properties = " + overrideSystemProps);
|
||||||
for (Map.Entry<String, ?> entry : overrideSystemProps.entrySet()) {
|
for (Map.Entry<String, ?> entry : overrideSystemProps.entrySet()) {
|
||||||
systemProps.put(entry.getKey(), entry.getValue().toString());
|
systemProps.put(entry.getKey(), entry.getValue().toString());
|
||||||
@ -207,18 +205,15 @@ public class CordaCaplet extends Capsule {
|
|||||||
} else return super.attribute(attr);
|
} else return super.attribute(attr);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void augmentClasspath(List<Path> classpath, File dir) {
|
private void augmentClasspath(List<Path> classpath, Path dir) {
|
||||||
try {
|
if (Files.exists(dir)) {
|
||||||
if (dir.exists()) {
|
try (var files = Files.list(dir)) {
|
||||||
// The following might return null if the directory is not there (we check this already) or if an I/O error occurs.
|
files.forEach((file) -> addToClasspath(classpath, file));
|
||||||
for (File file : dir.listFiles()) {
|
} catch (IOException e) {
|
||||||
addToClasspath(classpath, file);
|
log(LOG_QUIET, e);
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log(LOG_VERBOSE, "Directory to add in Classpath was not found " + dir.getAbsolutePath());
|
|
||||||
}
|
}
|
||||||
} catch (SecurityException | NullPointerException e) {
|
} else {
|
||||||
log(LOG_QUIET, e);
|
log(LOG_VERBOSE, "Directory to add in Classpath was not found " + dir.toAbsolutePath());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -230,14 +225,6 @@ public class CordaCaplet extends Capsule {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isAtLeastJavaVersion11() {
|
|
||||||
String version = System.getProperty("java.specification.version");
|
|
||||||
if (version != null) {
|
|
||||||
return Float.parseFloat(version) >= 11f;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Boolean checkIfCordappDirExists(File dir) {
|
private Boolean checkIfCordappDirExists(File dir) {
|
||||||
try {
|
try {
|
||||||
if (!dir.mkdir() && !dir.exists()) { // It is unlikely to enter this if-branch, but just in case.
|
if (!dir.mkdir() && !dir.exists()) { // It is unlikely to enter this if-branch, but just in case.
|
||||||
@ -256,18 +243,18 @@ public class CordaCaplet extends Capsule {
|
|||||||
log(LOG_VERBOSE, "Cordapps dir could not be created");
|
log(LOG_VERBOSE, "Cordapps dir could not be created");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addToClasspath(List<Path> classpath, File file) {
|
private void addToClasspath(List<Path> classpath, Path file) {
|
||||||
try {
|
try {
|
||||||
if (file.canRead()) {
|
if (Files.isReadable(file)) {
|
||||||
if (file.isFile() && isJAR(file)) {
|
if (Files.isRegularFile(file) && isJAR(file)) {
|
||||||
classpath.add(file.toPath().toAbsolutePath());
|
classpath.add(file.toAbsolutePath());
|
||||||
} else if (file.isDirectory()) { // Search in nested folders as well. TODO: check for circular symlinks.
|
} else if (Files.isDirectory(file)) { // Search in nested folders as well. TODO: check for circular symlinks.
|
||||||
augmentClasspath(classpath, file);
|
augmentClasspath(classpath, file);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log(LOG_VERBOSE, "File or directory to add in Classpath could not be read " + file.getAbsolutePath());
|
log(LOG_VERBOSE, "File or directory to add in Classpath could not be read " + file.toAbsolutePath());
|
||||||
}
|
}
|
||||||
} catch (SecurityException | NullPointerException e) {
|
} catch (SecurityException e) {
|
||||||
log(LOG_QUIET, e);
|
log(LOG_QUIET, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -280,30 +267,14 @@ public class CordaCaplet extends Capsule {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private Boolean isJAR(File file) {
|
private Boolean isJAR(Path file) {
|
||||||
return file.getName().toLowerCase().endsWith(".jar");
|
return file.toString().toLowerCase().endsWith(".jar");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper class so that we can parse the "systemProperties" element of node.conf.
|
* Helper class so that we can parse the "systemProperties" element of node.conf.
|
||||||
*/
|
*/
|
||||||
private static class Property {
|
private record Property(String key, Object value) {
|
||||||
private final String key;
|
|
||||||
private final Object value;
|
|
||||||
|
|
||||||
Property(String key, Object value) {
|
|
||||||
this.key = key;
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
String getKey() {
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
Object getValue() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Property create(Map.Entry<String, ConfigValue> entry) {
|
static Property create(Map.Entry<String, ConfigValue> entry) {
|
||||||
// String.join is preferred here over Typesafe's joinPath method, as the joinPath method would put quotes around the system
|
// String.join is preferred here over Typesafe's joinPath method, as the joinPath method would put quotes around the system
|
||||||
// property key which is undesirable here.
|
// property key which is undesirable here.
|
||||||
|
9
node/capsule/src/main/resources/node-jvm-args.txt
Normal file
9
node/capsule/src/main/resources/node-jvm-args.txt
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
--add-opens=java.base/java.lang=ALL-UNNAMED
|
||||||
|
--add-opens=java.base/java.lang.invoke=ALL-UNNAMED
|
||||||
|
--add-opens=java.base/java.nio=ALL-UNNAMED
|
||||||
|
--add-opens=java.base/java.security=ALL-UNNAMED
|
||||||
|
--add-opens=java.base/java.security.cert=ALL-UNNAMED
|
||||||
|
--add-opens=java.base/java.time=ALL-UNNAMED
|
||||||
|
--add-opens=java.base/java.util=ALL-UNNAMED
|
||||||
|
--add-opens=java.base/java.util.concurrent=ALL-UNNAMED
|
||||||
|
--add-opens=java.sql/java.sql=ALL-UNNAMED
|
@ -1,53 +1,12 @@
|
|||||||
package net.corda.node
|
package net.corda.node
|
||||||
|
|
||||||
import net.corda.core.internal.DeclaredField
|
import java.io.ObjectInputFilter
|
||||||
import net.corda.core.internal.staticField
|
import java.io.ObjectInputFilter.Status
|
||||||
import net.corda.node.internal.Node
|
|
||||||
import java.lang.reflect.Method
|
|
||||||
import java.lang.reflect.Proxy
|
|
||||||
|
|
||||||
internal object SerialFilter {
|
internal object SerialFilter {
|
||||||
private val filterInterface: Class<*>
|
|
||||||
private val serialClassGetter: Method
|
|
||||||
private val undecided: Any
|
|
||||||
private val rejected: Any
|
|
||||||
private val serialFilterLock: Any
|
|
||||||
private val serialFilterField: DeclaredField<Any>
|
|
||||||
|
|
||||||
init {
|
|
||||||
// ObjectInputFilter and friends are in java.io in Java 9 but sun.misc in backports:
|
|
||||||
fun getFilterInterface(packageName: String): Class<*>? {
|
|
||||||
return try {
|
|
||||||
Class.forName("$packageName.ObjectInputFilter")
|
|
||||||
} catch (e: ClassNotFoundException) {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// JDK 8u121 is the earliest JDK8 JVM that supports this functionality.
|
|
||||||
filterInterface = getFilterInterface("java.io")
|
|
||||||
?: getFilterInterface("sun.misc")
|
|
||||||
?: Node.failStartUp("Corda forbids Java deserialisation. Please upgrade to at least JDK 8u121.")
|
|
||||||
serialClassGetter = Class.forName("${filterInterface.name}\$FilterInfo").getMethod("serialClass")
|
|
||||||
val statusEnum = Class.forName("${filterInterface.name}\$Status")
|
|
||||||
undecided = statusEnum.getField("UNDECIDED").get(null)
|
|
||||||
rejected = statusEnum.getField("REJECTED").get(null)
|
|
||||||
val configClass = Class.forName("${filterInterface.name}\$Config")
|
|
||||||
serialFilterLock = configClass.staticField<Any>("serialFilterLock").value
|
|
||||||
serialFilterField = configClass.staticField("serialFilter")
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun install(acceptClass: (Class<*>) -> Boolean) {
|
internal fun install(acceptClass: (Class<*>) -> Boolean) {
|
||||||
val filter = Proxy.newProxyInstance(javaClass.classLoader, arrayOf(filterInterface)) { _, _, args ->
|
ObjectInputFilter.Config.setSerialFilter { filterInfo ->
|
||||||
val serialClass = serialClassGetter.invoke(args[0]) as Class<*>?
|
if (applyPredicate(acceptClass, filterInfo.serialClass())) Status.UNDECIDED else Status.REJECTED
|
||||||
if (applyPredicate(acceptClass, serialClass)) {
|
|
||||||
undecided
|
|
||||||
} else {
|
|
||||||
rejected
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Can't simply use the setter as in non-trampoline mode Capsule has inited the filter in premain:
|
|
||||||
synchronized(serialFilterLock) {
|
|
||||||
serialFilterField.value = filter
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,6 +36,7 @@ import java.io.DataInputStream
|
|||||||
import java.io.DataOutputStream
|
import java.io.DataOutputStream
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.lang.ProcessBuilder.Redirect
|
import java.lang.ProcessBuilder.Redirect
|
||||||
|
import java.lang.management.ManagementFactory
|
||||||
import java.net.ServerSocket
|
import java.net.ServerSocket
|
||||||
import java.net.Socket
|
import java.net.Socket
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
@ -179,15 +180,18 @@ class ExternalVerifierHandle(private val serviceHub: ServiceHubInternal) : AutoC
|
|||||||
val fromVerifier: DataInputStream
|
val fromVerifier: DataInputStream
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val logsDirectory = (serviceHub.configuration.baseDirectory / "logs").createDirectories()
|
val inheritedJvmArgs = ManagementFactory.getRuntimeMXBean().inputArguments.filter { "--add-opens" in it }
|
||||||
val command = listOf(
|
val command = ArrayList<String>()
|
||||||
"${Path(System.getProperty("java.home"), "bin", "java")}",
|
command += "${Path(System.getProperty("java.home"), "bin", "java")}"
|
||||||
|
command += inheritedJvmArgs
|
||||||
|
command += listOf(
|
||||||
"-jar",
|
"-jar",
|
||||||
"$verifierJar",
|
"$verifierJar",
|
||||||
"${server.localPort}",
|
"${server.localPort}",
|
||||||
System.getProperty("log4j2.level")?.lowercase() ?: "info" // TODO
|
System.getProperty("log4j2.level")?.lowercase() ?: "info"
|
||||||
)
|
)
|
||||||
log.debug { "Verifier command: $command" }
|
log.debug { "Verifier command: $command" }
|
||||||
|
val logsDirectory = (serviceHub.configuration.baseDirectory / "logs").createDirectories()
|
||||||
verifierProcess = ProcessBuilder(command)
|
verifierProcess = ProcessBuilder(command)
|
||||||
.redirectOutput(Redirect.appendTo((logsDirectory / "verifier-stdout.log").toFile()))
|
.redirectOutput(Redirect.appendTo((logsDirectory / "verifier-stdout.log").toFile()))
|
||||||
.redirectError(Redirect.appendTo((logsDirectory / "verifier-stderr.log").toFile()))
|
.redirectError(Redirect.appendTo((logsDirectory / "verifier-stderr.log").toFile()))
|
||||||
|
@ -78,9 +78,6 @@ dependencies {
|
|||||||
task integrationTest(type: Test, dependsOn: []) {
|
task integrationTest(type: Test, dependsOn: []) {
|
||||||
testClassesDirs = sourceSets.integrationTest.output.classesDirs
|
testClassesDirs = sourceSets.integrationTest.output.classesDirs
|
||||||
classpath = sourceSets.integrationTest.runtimeClasspath
|
classpath = sourceSets.integrationTest.runtimeClasspath
|
||||||
|
|
||||||
jvmArgs test_add_opens
|
|
||||||
jvmArgs test_add_exports
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def nodeTask = tasks.getByPath(':node:capsule:assemble')
|
def nodeTask = tasks.getByPath(':node:capsule:assemble')
|
||||||
@ -147,9 +144,6 @@ task runSender(type: JavaExec, dependsOn: jar) {
|
|||||||
classpath = sourceSets.main.runtimeClasspath
|
classpath = sourceSets.main.runtimeClasspath
|
||||||
main = 'net.corda.attachmentdemo.AttachmentDemoKt'
|
main = 'net.corda.attachmentdemo.AttachmentDemoKt'
|
||||||
|
|
||||||
jvmArgs test_add_opens
|
|
||||||
jvmArgs test_add_exports
|
|
||||||
|
|
||||||
args '--role'
|
args '--role'
|
||||||
args 'SENDER'
|
args 'SENDER'
|
||||||
}
|
}
|
||||||
@ -158,9 +152,6 @@ task runRecipient(type: JavaExec, dependsOn: jar) {
|
|||||||
classpath = sourceSets.main.runtimeClasspath
|
classpath = sourceSets.main.runtimeClasspath
|
||||||
main = 'net.corda.attachmentdemo.AttachmentDemoKt'
|
main = 'net.corda.attachmentdemo.AttachmentDemoKt'
|
||||||
|
|
||||||
jvmArgs test_add_opens
|
|
||||||
jvmArgs test_add_exports
|
|
||||||
|
|
||||||
args '--role'
|
args '--role'
|
||||||
args 'RECIPIENT'
|
args 'RECIPIENT'
|
||||||
}
|
}
|
||||||
|
@ -5,28 +5,23 @@ import net.corda.core.utilities.getOrThrow
|
|||||||
import net.corda.node.services.Permissions.Companion.all
|
import net.corda.node.services.Permissions.Companion.all
|
||||||
import net.corda.testing.core.DUMMY_BANK_A_NAME
|
import net.corda.testing.core.DUMMY_BANK_A_NAME
|
||||||
import net.corda.testing.core.DUMMY_BANK_B_NAME
|
import net.corda.testing.core.DUMMY_BANK_B_NAME
|
||||||
import net.corda.testing.core.DUMMY_NOTARY_NAME
|
|
||||||
import net.corda.testing.driver.DriverParameters
|
import net.corda.testing.driver.DriverParameters
|
||||||
import net.corda.testing.driver.driver
|
import net.corda.testing.driver.driver
|
||||||
import net.corda.testing.driver.internal.incrementalPortAllocation
|
import net.corda.testing.driver.internal.incrementalPortAllocation
|
||||||
import net.corda.testing.node.NotarySpec
|
|
||||||
import net.corda.testing.node.User
|
import net.corda.testing.node.User
|
||||||
import net.corda.testing.node.internal.DummyClusterSpec
|
|
||||||
import net.corda.testing.node.internal.findCordapp
|
import net.corda.testing.node.internal.findCordapp
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.util.concurrent.CompletableFuture.supplyAsync
|
import java.util.concurrent.CompletableFuture.supplyAsync
|
||||||
|
|
||||||
class AttachmentDemoTest {
|
class AttachmentDemoTest {
|
||||||
// run with a 10,000,000 bytes in-memory zip file. In practice, a slightly bigger file will be used (~10,002,000 bytes).
|
|
||||||
@Test(timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
fun `attachment demo using a 10MB zip file`() {
|
fun `attachment demo using a 10MB zip file`() {
|
||||||
val numOfExpectedBytes = 10_000_000
|
val numOfExpectedBytes = 10_000_000
|
||||||
driver(DriverParameters(
|
driver(DriverParameters(
|
||||||
portAllocation = incrementalPortAllocation(),
|
portAllocation = incrementalPortAllocation(),
|
||||||
startNodesInProcess = true,
|
startNodesInProcess = true,
|
||||||
cordappsForAllNodes = listOf(findCordapp("net.corda.attachmentdemo.contracts"), findCordapp("net.corda.attachmentdemo.workflows")),
|
cordappsForAllNodes = listOf(findCordapp("net.corda.attachmentdemo.contracts"), findCordapp("net.corda.attachmentdemo.workflows"))
|
||||||
notarySpecs = listOf(NotarySpec(name = DUMMY_NOTARY_NAME, cluster = DummyClusterSpec(clusterSize = 1))))
|
)) {
|
||||||
) {
|
|
||||||
val demoUser = listOf(User("demo", "demo", setOf(all())))
|
val demoUser = listOf(User("demo", "demo", setOf(all())))
|
||||||
val (nodeA, nodeB) = listOf(
|
val (nodeA, nodeB) = listOf(
|
||||||
startNode(providedName = DUMMY_BANK_A_NAME, rpcUsers = demoUser, maximumHeapSize = "1g"),
|
startNode(providedName = DUMMY_BANK_A_NAME, rpcUsers = demoUser, maximumHeapSize = "1g"),
|
||||||
|
@ -7,7 +7,7 @@ import net.corda.client.rpc.CordaRPCClient
|
|||||||
import net.corda.core.crypto.SecureHash
|
import net.corda.core.crypto.SecureHash
|
||||||
import net.corda.core.identity.CordaX500Name
|
import net.corda.core.identity.CordaX500Name
|
||||||
import net.corda.core.internal.Emoji
|
import net.corda.core.internal.Emoji
|
||||||
import net.corda.core.internal.InputStreamAndHash
|
import net.corda.core.internal.hash
|
||||||
import net.corda.core.messaging.CordaRPCOps
|
import net.corda.core.messaging.CordaRPCOps
|
||||||
import net.corda.core.messaging.startTrackedFlow
|
import net.corda.core.messaging.startTrackedFlow
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
@ -16,9 +16,14 @@ import java.io.InputStream
|
|||||||
import java.net.HttpURLConnection
|
import java.net.HttpURLConnection
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.util.jar.JarInputStream
|
import java.util.jar.JarInputStream
|
||||||
|
import java.util.zip.ZipEntry
|
||||||
|
import java.util.zip.ZipOutputStream
|
||||||
import javax.servlet.http.HttpServletResponse.SC_OK
|
import javax.servlet.http.HttpServletResponse.SC_OK
|
||||||
import javax.ws.rs.core.HttpHeaders.CONTENT_DISPOSITION
|
import javax.ws.rs.core.HttpHeaders.CONTENT_DISPOSITION
|
||||||
import javax.ws.rs.core.MediaType.APPLICATION_OCTET_STREAM
|
import javax.ws.rs.core.MediaType.APPLICATION_OCTET_STREAM
|
||||||
|
import kotlin.io.path.createTempFile
|
||||||
|
import kotlin.io.path.inputStream
|
||||||
|
import kotlin.io.path.outputStream
|
||||||
import kotlin.system.exitProcess
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
internal enum class Role {
|
internal enum class Role {
|
||||||
@ -57,11 +62,16 @@ fun main(args: Array<String>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** An in memory test zip attachment of at least numOfClearBytes size, will be used. */
|
/** A temp zip file attachment of at least numOfClearBytes size, will be used. */
|
||||||
// DOCSTART 2
|
// DOCSTART 2
|
||||||
fun sender(rpc: CordaRPCOps, numOfClearBytes: Int = 1024) { // default size 1K.
|
fun sender(rpc: CordaRPCOps, numOfClearBytes: Int = 1024) { // default size 1K.
|
||||||
val (inputStream, hash) = InputStreamAndHash.createInMemoryTestZip(numOfClearBytes, 0)
|
val attachmentFile = createTempFile("attachment-demo").apply { toFile().deleteOnExit() }
|
||||||
sender(rpc, inputStream, hash)
|
ZipOutputStream(attachmentFile.outputStream()).use { zip ->
|
||||||
|
zip.putNextEntry(ZipEntry("test"))
|
||||||
|
zip.write(ByteArray(numOfClearBytes))
|
||||||
|
zip.closeEntry()
|
||||||
|
}
|
||||||
|
sender(rpc, attachmentFile.inputStream(), attachmentFile.hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun sender(rpc: CordaRPCOps, inputStream: InputStream, hash: SecureHash.SHA256) {
|
private fun sender(rpc: CordaRPCOps, inputStream: InputStream, hash: SecureHash.SHA256) {
|
||||||
|
@ -117,36 +117,24 @@ tasks.register('runRPCCashIssue', JavaExec) {
|
|||||||
classpath = sourceSets.main.runtimeClasspath
|
classpath = sourceSets.main.runtimeClasspath
|
||||||
mainClass = 'net.corda.bank.IssueCash'
|
mainClass = 'net.corda.bank.IssueCash'
|
||||||
|
|
||||||
jvmArgs test_add_opens
|
|
||||||
jvmArgs test_add_exports
|
|
||||||
|
|
||||||
args '--role'
|
args '--role'
|
||||||
args 'ISSUE_CASH_RPC'
|
args 'ISSUE_CASH_RPC'
|
||||||
args '--quantity'
|
args '--quantity'
|
||||||
args 20000
|
args 20000
|
||||||
args '--currency'
|
args '--currency'
|
||||||
args 'USD'
|
args 'USD'
|
||||||
|
|
||||||
jvmArgs test_add_opens
|
|
||||||
jvmArgs test_add_exports
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.register('runWebCashIssue', JavaExec) {
|
tasks.register('runWebCashIssue', JavaExec) {
|
||||||
classpath = sourceSets.main.runtimeClasspath
|
classpath = sourceSets.main.runtimeClasspath
|
||||||
mainClass = 'net.corda.bank.IssueCash'
|
mainClass = 'net.corda.bank.IssueCash'
|
||||||
|
|
||||||
jvmArgs test_add_opens
|
|
||||||
jvmArgs test_add_exports
|
|
||||||
|
|
||||||
args '--role'
|
args '--role'
|
||||||
args 'ISSUE_CASH_WEB'
|
args 'ISSUE_CASH_WEB'
|
||||||
args '--quantity'
|
args '--quantity'
|
||||||
args 30000
|
args 30000
|
||||||
args '--currency'
|
args '--currency'
|
||||||
args 'GBP'
|
args 'GBP'
|
||||||
|
|
||||||
jvmArgs test_add_opens
|
|
||||||
jvmArgs test_add_exports
|
|
||||||
}
|
}
|
||||||
|
|
||||||
jar {
|
jar {
|
||||||
|
@ -170,9 +170,6 @@ task deployNodes(type: net.corda.plugins.Cordform) {
|
|||||||
task integrationTest(type: Test, dependsOn: []) {
|
task integrationTest(type: Test, dependsOn: []) {
|
||||||
testClassesDirs = sourceSets.integrationTest.output.classesDirs
|
testClassesDirs = sourceSets.integrationTest.output.classesDirs
|
||||||
classpath = sourceSets.integrationTest.runtimeClasspath
|
classpath = sourceSets.integrationTest.runtimeClasspath
|
||||||
|
|
||||||
jvmArgs test_add_opens
|
|
||||||
jvmArgs test_add_exports
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cordapp {
|
cordapp {
|
||||||
|
@ -73,9 +73,6 @@ dependencies {
|
|||||||
task integrationTest(type: Test, dependsOn: []) {
|
task integrationTest(type: Test, dependsOn: []) {
|
||||||
testClassesDirs = sourceSets.integrationTest.output.classesDirs
|
testClassesDirs = sourceSets.integrationTest.output.classesDirs
|
||||||
classpath = sourceSets.integrationTest.runtimeClasspath
|
classpath = sourceSets.integrationTest.runtimeClasspath
|
||||||
|
|
||||||
jvmArgs test_add_opens
|
|
||||||
jvmArgs test_add_exports
|
|
||||||
}
|
}
|
||||||
|
|
||||||
configurations.cordaCordapp.canBeResolved = true
|
configurations.cordaCordapp.canBeResolved = true
|
||||||
@ -149,16 +146,6 @@ task deployNodes(type: net.corda.plugins.Cordform) {
|
|||||||
|
|
||||||
jar {
|
jar {
|
||||||
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||||
manifest {
|
|
||||||
attributes('Add-Opens': 'java.management/com.sun.jmx.mbeanserver ' +
|
|
||||||
'java.base/java.time java.base/java.io ' +
|
|
||||||
'java.base/java.util java.base/java.net ' +
|
|
||||||
'java.base/java.nio java.base/java.lang.invoke ' +
|
|
||||||
'java.base/java.security.cert java.base/java.security ' +
|
|
||||||
'java.base/javax.net.ssl java.base/java.util.concurrent ' +
|
|
||||||
'java.sql/java.sql'
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
idea {
|
idea {
|
||||||
|
@ -54,7 +54,6 @@ import org.apache.qpid.proton.codec.DecoderImpl
|
|||||||
import org.apache.qpid.proton.codec.EncoderImpl
|
import org.apache.qpid.proton.codec.EncoderImpl
|
||||||
import org.assertj.core.api.Assertions.assertThat
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.assertj.core.api.Assertions.assertThatExceptionOfType
|
import org.assertj.core.api.Assertions.assertThatExceptionOfType
|
||||||
import org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
|
||||||
import org.assertj.core.api.Assertions.assertThatThrownBy
|
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||||
import org.assertj.core.api.Assertions.catchThrowable
|
import org.assertj.core.api.Assertions.catchThrowable
|
||||||
import org.bouncycastle.asn1.x500.X500Name
|
import org.bouncycastle.asn1.x500.X500Name
|
||||||
@ -73,6 +72,7 @@ import org.junit.runners.Parameterized.Parameters
|
|||||||
import org.mockito.kotlin.doReturn
|
import org.mockito.kotlin.doReturn
|
||||||
import org.mockito.kotlin.whenever
|
import org.mockito.kotlin.whenever
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
import java.io.InputStream
|
||||||
import java.io.NotSerializableException
|
import java.io.NotSerializableException
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
import java.math.BigInteger
|
import java.math.BigInteger
|
||||||
@ -148,13 +148,13 @@ class SerializationOutputTests(private val compression: CordaSerializationEncodi
|
|||||||
|
|
||||||
data class Foo(val bar: String, val pub: Int)
|
data class Foo(val bar: String, val pub: Int)
|
||||||
|
|
||||||
data class testFloat(val f: Float)
|
data class TestFloat(val f: Float)
|
||||||
|
|
||||||
data class testDouble(val d: Double)
|
data class TestDouble(val d: Double)
|
||||||
|
|
||||||
data class testShort(val s: Short)
|
data class TestShort(val s: Short)
|
||||||
|
|
||||||
data class testBoolean(val b: Boolean)
|
data class TestBoolean(val b: Boolean)
|
||||||
|
|
||||||
interface FooInterface {
|
interface FooInterface {
|
||||||
val pub: Int
|
val pub: Int
|
||||||
@ -340,25 +340,25 @@ class SerializationOutputTests(private val compression: CordaSerializationEncodi
|
|||||||
|
|
||||||
@Test(timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
fun `test float`() {
|
fun `test float`() {
|
||||||
val obj = testFloat(10.0F)
|
val obj = TestFloat(10.0F)
|
||||||
serdes(obj)
|
serdes(obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
fun `test double`() {
|
fun `test double`() {
|
||||||
val obj = testDouble(10.0)
|
val obj = TestDouble(10.0)
|
||||||
serdes(obj)
|
serdes(obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
fun `test short`() {
|
fun `test short`() {
|
||||||
val obj = testShort(1)
|
val obj = TestShort(1)
|
||||||
serdes(obj)
|
serdes(obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
fun `test bool`() {
|
fun `test bool`() {
|
||||||
val obj = testBoolean(true)
|
val obj = TestBoolean(true)
|
||||||
serdes(obj)
|
serdes(obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -377,7 +377,7 @@ class SerializationOutputTests(private val compression: CordaSerializationEncodi
|
|||||||
@Test(timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
fun `test dislike of HashMap`() {
|
fun `test dislike of HashMap`() {
|
||||||
val obj = WrapHashMap(HashMap())
|
val obj = WrapHashMap(HashMap())
|
||||||
assertThatIllegalArgumentException().isThrownBy {
|
assertThatExceptionOfType(NotSerializableException::class.java).isThrownBy {
|
||||||
serdes(obj)
|
serdes(obj)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1303,7 +1303,7 @@ class SerializationOutputTests(private val compression: CordaSerializationEncodi
|
|||||||
)
|
)
|
||||||
factory2.register(net.corda.serialization.internal.amqp.custom.InputStreamSerializer)
|
factory2.register(net.corda.serialization.internal.amqp.custom.InputStreamSerializer)
|
||||||
val bytes = ByteArray(10) { it.toByte() }
|
val bytes = ByteArray(10) { it.toByte() }
|
||||||
val obj = bytes.inputStream()
|
val obj: InputStream = bytes.inputStream()
|
||||||
val obj2 = serdes(obj, factory, factory2, expectedEqual = false, expectDeserializedEqual = false)
|
val obj2 = serdes(obj, factory, factory2, expectedEqual = false, expectDeserializedEqual = false)
|
||||||
val obj3 = bytes.inputStream() // Can't use original since the stream pointer has moved.
|
val obj3 = bytes.inputStream() // Can't use original since the stream pointer has moved.
|
||||||
assertEquals(obj3.available(), obj2.available())
|
assertEquals(obj3.available(), obj2.available())
|
||||||
|
@ -57,10 +57,6 @@ artifacts {
|
|||||||
jar {
|
jar {
|
||||||
archiveBaseName = 'corda-serialization'
|
archiveBaseName = 'corda-serialization'
|
||||||
archiveClassifier = ''
|
archiveClassifier = ''
|
||||||
|
|
||||||
manifest {
|
|
||||||
attributes('Add-Opens': 'java.base/java.time java.base/java.io')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
publishing {
|
publishing {
|
||||||
|
@ -43,14 +43,8 @@ class ByteBufferInputStream(val byteBuffer: ByteBuffer) : InputStream() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class ByteBufferOutputStream(size: Int) : ByteArrayOutputStream(size) {
|
class ByteBufferOutputStream(size: Int) : ByteArrayOutputStream(size) {
|
||||||
companion object {
|
|
||||||
private val ensureCapacity = ByteArrayOutputStream::class.java.getDeclaredMethod("ensureCapacity", Int::class.java).apply {
|
|
||||||
isAccessible = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <T> alsoAsByteBuffer(remaining: Int, task: (ByteBuffer) -> T): T {
|
fun <T> alsoAsByteBuffer(remaining: Int, task: (ByteBuffer) -> T): T {
|
||||||
ensureCapacity.invoke(this, count + remaining)
|
ensureCapacity(count + remaining)
|
||||||
val buffer = ByteBuffer.wrap(buf, count, remaining)
|
val buffer = ByteBuffer.wrap(buf, count, remaining)
|
||||||
val result = task(buffer)
|
val result = task(buffer)
|
||||||
count = buffer.position()
|
count = buffer.position()
|
||||||
@ -60,4 +54,10 @@ class ByteBufferOutputStream(size: Int) : ByteArrayOutputStream(size) {
|
|||||||
fun copyTo(stream: OutputStream) {
|
fun copyTo(stream: OutputStream) {
|
||||||
stream.write(buf, 0, count)
|
stream.write(buf, 0, count)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun ensureCapacity(minCapacity: Int) {
|
||||||
|
if (minCapacity > buf.size) {
|
||||||
|
buf = buf.copyOf(minCapacity)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
package net.corda.serialization.internal
|
||||||
|
|
||||||
|
import java.io.NotSerializableException
|
||||||
|
|
||||||
|
@Suppress("FunctionNaming")
|
||||||
|
fun NotSerializableException(message: String?, cause: Throwable?): NotSerializableException {
|
||||||
|
return NotSerializableException(message).apply { initCause(cause) }
|
||||||
|
}
|
@ -1,19 +1,11 @@
|
|||||||
package net.corda.serialization.internal.amqp
|
package net.corda.serialization.internal.amqp
|
||||||
|
|
||||||
import net.corda.core.internal.VisibleForTesting
|
import net.corda.core.internal.VisibleForTesting
|
||||||
|
import net.corda.serialization.internal.NotSerializableException
|
||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
import java.io.NotSerializableException
|
import java.io.NotSerializableException
|
||||||
import java.lang.reflect.Type
|
import java.lang.reflect.Type
|
||||||
|
|
||||||
/**
|
|
||||||
* Not a public property so will have to use reflection
|
|
||||||
*/
|
|
||||||
private fun Throwable.setMessage(newMsg: String) {
|
|
||||||
val detailMessageField = Throwable::class.java.getDeclaredField("detailMessage")
|
|
||||||
detailMessageField.isAccessible = true
|
|
||||||
detailMessageField.set(this, newMsg)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility function which helps tracking the path in the object graph when exceptions are thrown.
|
* Utility function which helps tracking the path in the object graph when exceptions are thrown.
|
||||||
* Since there might be a chain of nested calls it is useful to record which part of the graph caused an issue.
|
* Since there might be a chain of nested calls it is useful to record which part of the graph caused an issue.
|
||||||
@ -22,15 +14,13 @@ private fun Throwable.setMessage(newMsg: String) {
|
|||||||
internal inline fun <T> ifThrowsAppend(strToAppendFn: () -> String, block: () -> T): T {
|
internal inline fun <T> ifThrowsAppend(strToAppendFn: () -> String, block: () -> T): T {
|
||||||
try {
|
try {
|
||||||
return block()
|
return block()
|
||||||
} catch (th: Throwable) {
|
} catch (e: AMQPNotSerializableException) {
|
||||||
when (th) {
|
e.classHierarchy += strToAppendFn()
|
||||||
is AMQPNotSerializableException -> th.classHierarchy.add(strToAppendFn())
|
throw e
|
||||||
// Do not overwrite the message of these exceptions as it may be used.
|
} catch (e: Exception) {
|
||||||
is ClassNotFoundException -> {}
|
// Avoid creating heavily nested NotSerializableExceptions
|
||||||
is NoClassDefFoundError -> {}
|
val cause = if (e.message?.contains(" -> ") == true) { e.cause ?: e } else { e }
|
||||||
else -> th.setMessage("${strToAppendFn()} -> ${th.message}")
|
throw NotSerializableException("${strToAppendFn()} -> ${e.message}", cause)
|
||||||
}
|
|
||||||
throw th
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,8 +67,3 @@ open class AMQPNotSerializableException(
|
|||||||
logger.debug("", cause)
|
logger.debug("", cause)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SyntheticParameterException(type: Type) : AMQPNotSerializableException(
|
|
||||||
type,
|
|
||||||
"Type '${type.typeName} has synthetic "
|
|
||||||
+ "fields and is likely a nested inner class. This is not support by the Corda AMQP serialization framework")
|
|
@ -11,6 +11,7 @@ import net.corda.core.utilities.loggerFor
|
|||||||
import net.corda.core.utilities.trace
|
import net.corda.core.utilities.trace
|
||||||
import net.corda.serialization.internal.ByteBufferInputStream
|
import net.corda.serialization.internal.ByteBufferInputStream
|
||||||
import net.corda.serialization.internal.CordaSerializationEncoding
|
import net.corda.serialization.internal.CordaSerializationEncoding
|
||||||
|
import net.corda.serialization.internal.NotSerializableException
|
||||||
import net.corda.serialization.internal.NullEncodingWhitelist
|
import net.corda.serialization.internal.NullEncodingWhitelist
|
||||||
import net.corda.serialization.internal.SectionId
|
import net.corda.serialization.internal.SectionId
|
||||||
import net.corda.serialization.internal.encodingNotPermittedFormat
|
import net.corda.serialization.internal.encodingNotPermittedFormat
|
||||||
@ -120,11 +121,11 @@ class DeserializationInput constructor(
|
|||||||
return generator()
|
return generator()
|
||||||
} catch (amqp : AMQPNotSerializableException) {
|
} catch (amqp : AMQPNotSerializableException) {
|
||||||
amqp.log("Deserialize", logger)
|
amqp.log("Deserialize", logger)
|
||||||
throw NotSerializableException(amqp.mitigation)
|
throw NotSerializableException(amqp.mitigation, amqp)
|
||||||
} catch (nse: NotSerializableException) {
|
} catch (nse: NotSerializableException) {
|
||||||
throw nse
|
throw nse
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
throw NotSerializableException("Internal deserialization failure: ${e.javaClass.name}: ${e.message}").apply { initCause(e) }
|
throw NotSerializableException("Internal deserialization failure: ${e.javaClass.name}: ${e.message}", e)
|
||||||
} finally {
|
} finally {
|
||||||
objectHistory.clear()
|
objectHistory.clear()
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package net.corda.serialization.internal.amqp
|
package net.corda.serialization.internal.amqp
|
||||||
|
|
||||||
|
import net.corda.serialization.internal.NotSerializableException
|
||||||
import net.corda.serialization.internal.model.LocalConstructorInformation
|
import net.corda.serialization.internal.model.LocalConstructorInformation
|
||||||
import net.corda.serialization.internal.model.LocalPropertyInformation
|
import net.corda.serialization.internal.model.LocalPropertyInformation
|
||||||
import net.corda.serialization.internal.model.LocalTypeInformation
|
import net.corda.serialization.internal.model.LocalTypeInformation
|
||||||
@ -32,17 +33,12 @@ private class ConstructorCaller(private val javaConstructor: Constructor<Any>) :
|
|||||||
try {
|
try {
|
||||||
javaConstructor.newInstance(*parameters)
|
javaConstructor.newInstance(*parameters)
|
||||||
} catch (e: InvocationTargetException) {
|
} catch (e: InvocationTargetException) {
|
||||||
@Suppress("DEPRECATION") // JDK11: isAccessible() should be replaced with canAccess() (since 9)
|
|
||||||
throw NotSerializableException(
|
throw NotSerializableException(
|
||||||
"Constructor for ${javaConstructor.declaringClass} (isAccessible=${javaConstructor.isAccessible}) " +
|
"Constructor for ${javaConstructor.declaringClass.name} failed when called with parameters ${parameters.asList()}: ${e.cause?.message}",
|
||||||
"failed when called with parameters ${parameters.toList()}: ${e.cause!!.message}"
|
e.cause
|
||||||
)
|
)
|
||||||
} catch (e: IllegalAccessException) {
|
} catch (e: IllegalAccessException) {
|
||||||
@Suppress("DEPRECATION") // JDK11: isAccessible() should be replaced with canAccess() (since 9)
|
throw NotSerializableException("Constructor for ${javaConstructor.declaringClass.name} not accessible: ${e.message}")
|
||||||
throw NotSerializableException(
|
|
||||||
"Constructor for ${javaConstructor.declaringClass} (isAccessible=${javaConstructor.isAccessible}) " +
|
|
||||||
"not accessible: ${e.message}"
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,9 +2,9 @@ package net.corda.serialization.internal.amqp.custom
|
|||||||
|
|
||||||
import net.corda.core.serialization.DESERIALIZATION_CACHE_PROPERTY
|
import net.corda.core.serialization.DESERIALIZATION_CACHE_PROPERTY
|
||||||
import net.corda.core.serialization.SerializationContext
|
import net.corda.core.serialization.SerializationContext
|
||||||
|
import net.corda.serialization.internal.NotSerializableException
|
||||||
import net.corda.serialization.internal.amqp.CustomSerializer
|
import net.corda.serialization.internal.amqp.CustomSerializer
|
||||||
import net.corda.serialization.internal.amqp.SerializerFactory
|
import net.corda.serialization.internal.amqp.SerializerFactory
|
||||||
import java.io.NotSerializableException
|
|
||||||
import java.security.cert.CertPath
|
import java.security.cert.CertPath
|
||||||
import java.security.cert.CertificateException
|
import java.security.cert.CertificateException
|
||||||
import java.security.cert.CertificateFactory
|
import java.security.cert.CertificateFactory
|
||||||
@ -23,9 +23,7 @@ class CertPathSerializer(
|
|||||||
val cf = CertificateFactory.getInstance(proxy.type)
|
val cf = CertificateFactory.getInstance(proxy.type)
|
||||||
return cf.generateCertPath(proxy.encoded.inputStream())
|
return cf.generateCertPath(proxy.encoded.inputStream())
|
||||||
} catch (ce: CertificateException) {
|
} catch (ce: CertificateException) {
|
||||||
val nse = NotSerializableException("java.security.cert.CertPath: $type")
|
throw NotSerializableException("java.security.cert.CertPath: $type", ce)
|
||||||
nse.initCause(ce)
|
|
||||||
throw nse
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,12 +11,7 @@ import java.lang.reflect.Type
|
|||||||
/**
|
/**
|
||||||
* A serializer that writes out the content of an input stream as bytes and deserializes into a [ByteArrayInputStream].
|
* A serializer that writes out the content of an input stream as bytes and deserializes into a [ByteArrayInputStream].
|
||||||
*/
|
*/
|
||||||
object InputStreamSerializer
|
object InputStreamSerializer : CustomSerializer.Implements<InputStream>(InputStream::class.java) {
|
||||||
: CustomSerializer.Implements<InputStream>(
|
|
||||||
InputStream::class.java
|
|
||||||
) {
|
|
||||||
override val revealSubclassesInSchema: Boolean = true
|
|
||||||
|
|
||||||
override val schemaForDocumentation = Schema(
|
override val schemaForDocumentation = Schema(
|
||||||
listOf(
|
listOf(
|
||||||
RestrictedType(
|
RestrictedType(
|
||||||
|
@ -2,7 +2,6 @@ package net.corda.serialization.internal.amqp.custom
|
|||||||
|
|
||||||
import net.corda.serialization.internal.amqp.CustomSerializer
|
import net.corda.serialization.internal.amqp.CustomSerializer
|
||||||
import net.corda.serialization.internal.amqp.SerializerFactory
|
import net.corda.serialization.internal.amqp.SerializerFactory
|
||||||
import java.lang.reflect.Method
|
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.time.ZoneId
|
import java.time.ZoneId
|
||||||
import java.time.ZoneOffset
|
import java.time.ZoneOffset
|
||||||
@ -18,21 +17,6 @@ class ZonedDateTimeSerializer(
|
|||||||
ZonedDateTimeProxy::class.java,
|
ZonedDateTimeProxy::class.java,
|
||||||
factory
|
factory
|
||||||
) {
|
) {
|
||||||
// Java deserialization of `ZonedDateTime` uses a private method. We will resolve this somewhat statically
|
|
||||||
// so that any change to internals of `ZonedDateTime` is detected early.
|
|
||||||
companion object {
|
|
||||||
val ofLenient: Method = ZonedDateTime::class.java.getDeclaredMethod(
|
|
||||||
"ofLenient",
|
|
||||||
LocalDateTime::class.java,
|
|
||||||
ZoneOffset::class.java,
|
|
||||||
ZoneId::class.java
|
|
||||||
)
|
|
||||||
|
|
||||||
init {
|
|
||||||
ofLenient.isAccessible = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override val additionalSerializers: Iterable<CustomSerializer<out Any>> = listOf(
|
override val additionalSerializers: Iterable<CustomSerializer<out Any>> = listOf(
|
||||||
LocalDateTimeSerializer(factory),
|
LocalDateTimeSerializer(factory),
|
||||||
ZoneIdSerializer(factory)
|
ZoneIdSerializer(factory)
|
||||||
@ -40,12 +24,7 @@ class ZonedDateTimeSerializer(
|
|||||||
|
|
||||||
override fun toProxy(obj: ZonedDateTime): ZonedDateTimeProxy = ZonedDateTimeProxy(obj.toLocalDateTime(), obj.offset, obj.zone)
|
override fun toProxy(obj: ZonedDateTime): ZonedDateTimeProxy = ZonedDateTimeProxy(obj.toLocalDateTime(), obj.offset, obj.zone)
|
||||||
|
|
||||||
override fun fromProxy(proxy: ZonedDateTimeProxy): ZonedDateTime = ofLenient.invoke(
|
override fun fromProxy(proxy: ZonedDateTimeProxy): ZonedDateTime = ZonedDateTime.ofLocal(proxy.dateTime, proxy.zone, proxy.offset)
|
||||||
null,
|
|
||||||
proxy.dateTime,
|
|
||||||
proxy.offset,
|
|
||||||
proxy.zone
|
|
||||||
) as ZonedDateTime
|
|
||||||
|
|
||||||
data class ZonedDateTimeProxy(val dateTime: LocalDateTime, val offset: ZoneOffset, val zone: ZoneId)
|
data class ZonedDateTimeProxy(val dateTime: LocalDateTime, val offset: ZoneOffset, val zone: ZoneId)
|
||||||
}
|
}
|
||||||
|
@ -2,18 +2,25 @@ package net.corda.serialization.internal.model
|
|||||||
|
|
||||||
import net.corda.core.internal.isAbstractClass
|
import net.corda.core.internal.isAbstractClass
|
||||||
import net.corda.core.internal.isConcreteClass
|
import net.corda.core.internal.isConcreteClass
|
||||||
|
import net.corda.core.internal.isJdkClass
|
||||||
import net.corda.core.internal.kotlinObjectInstance
|
import net.corda.core.internal.kotlinObjectInstance
|
||||||
import net.corda.core.serialization.ConstructorForDeserialization
|
import net.corda.core.serialization.ConstructorForDeserialization
|
||||||
import net.corda.core.serialization.DeprecatedConstructorForDeserialization
|
import net.corda.core.serialization.DeprecatedConstructorForDeserialization
|
||||||
|
import net.corda.core.utilities.loggerFor
|
||||||
import net.corda.serialization.internal.NotSerializableDetailedException
|
import net.corda.serialization.internal.NotSerializableDetailedException
|
||||||
import net.corda.serialization.internal.amqp.*
|
import net.corda.serialization.internal.amqp.PropertyDescriptor
|
||||||
|
import net.corda.serialization.internal.amqp.TransformsAnnotationProcessor
|
||||||
|
import net.corda.serialization.internal.amqp.asClass
|
||||||
|
import net.corda.serialization.internal.amqp.calculatedPropertyDescriptors
|
||||||
|
import net.corda.serialization.internal.amqp.componentType
|
||||||
|
import net.corda.serialization.internal.amqp.propertyDescriptors
|
||||||
|
import net.corda.serialization.internal.model.LocalTypeInformation.ACollection
|
||||||
|
import net.corda.serialization.internal.model.LocalTypeInformation.AMap
|
||||||
import net.corda.serialization.internal.model.LocalTypeInformation.Abstract
|
import net.corda.serialization.internal.model.LocalTypeInformation.Abstract
|
||||||
import net.corda.serialization.internal.model.LocalTypeInformation.AnArray
|
import net.corda.serialization.internal.model.LocalTypeInformation.AnArray
|
||||||
import net.corda.serialization.internal.model.LocalTypeInformation.AnEnum
|
import net.corda.serialization.internal.model.LocalTypeInformation.AnEnum
|
||||||
import net.corda.serialization.internal.model.LocalTypeInformation.AnInterface
|
import net.corda.serialization.internal.model.LocalTypeInformation.AnInterface
|
||||||
import net.corda.serialization.internal.model.LocalTypeInformation.Atomic
|
import net.corda.serialization.internal.model.LocalTypeInformation.Atomic
|
||||||
import net.corda.serialization.internal.model.LocalTypeInformation.ACollection
|
|
||||||
import net.corda.serialization.internal.model.LocalTypeInformation.AMap
|
|
||||||
import net.corda.serialization.internal.model.LocalTypeInformation.Composable
|
import net.corda.serialization.internal.model.LocalTypeInformation.Composable
|
||||||
import net.corda.serialization.internal.model.LocalTypeInformation.Cycle
|
import net.corda.serialization.internal.model.LocalTypeInformation.Cycle
|
||||||
import net.corda.serialization.internal.model.LocalTypeInformation.NonComposable
|
import net.corda.serialization.internal.model.LocalTypeInformation.NonComposable
|
||||||
@ -22,11 +29,12 @@ import net.corda.serialization.internal.model.LocalTypeInformation.Singleton
|
|||||||
import net.corda.serialization.internal.model.LocalTypeInformation.Top
|
import net.corda.serialization.internal.model.LocalTypeInformation.Top
|
||||||
import net.corda.serialization.internal.model.LocalTypeInformation.Unknown
|
import net.corda.serialization.internal.model.LocalTypeInformation.Unknown
|
||||||
import java.io.NotSerializableException
|
import java.io.NotSerializableException
|
||||||
|
import java.lang.reflect.InaccessibleObjectException
|
||||||
import java.lang.reflect.Method
|
import java.lang.reflect.Method
|
||||||
import java.lang.reflect.ParameterizedType
|
import java.lang.reflect.ParameterizedType
|
||||||
import java.lang.reflect.Type
|
import java.lang.reflect.Type
|
||||||
import kotlin.collections.LinkedHashMap
|
|
||||||
import kotlin.reflect.KFunction
|
import kotlin.reflect.KFunction
|
||||||
|
import kotlin.reflect.KVisibility
|
||||||
import kotlin.reflect.full.findAnnotation
|
import kotlin.reflect.full.findAnnotation
|
||||||
import kotlin.reflect.full.memberProperties
|
import kotlin.reflect.full.memberProperties
|
||||||
import kotlin.reflect.full.primaryConstructor
|
import kotlin.reflect.full.primaryConstructor
|
||||||
@ -298,7 +306,7 @@ internal data class LocalTypeInformationBuilder(val lookup: LocalTypeLookup,
|
|||||||
private fun propertiesSatisfyConstructor(constructorInformation: LocalConstructorInformation, properties: Map<PropertyName, LocalPropertyInformation>): Boolean {
|
private fun propertiesSatisfyConstructor(constructorInformation: LocalConstructorInformation, properties: Map<PropertyName, LocalPropertyInformation>): Boolean {
|
||||||
if (!constructorInformation.hasParameters) return true
|
if (!constructorInformation.hasParameters) return true
|
||||||
|
|
||||||
val indicesAddressedByProperties = properties.values.asSequence().mapNotNullTo(LinkedHashSet()) {
|
val indicesAddressedByProperties = properties.values.mapNotNullTo(LinkedHashSet()) {
|
||||||
when (it) {
|
when (it) {
|
||||||
is LocalPropertyInformation.ConstructorPairedProperty -> it.constructorSlot.parameterIndex
|
is LocalPropertyInformation.ConstructorPairedProperty -> it.constructorSlot.parameterIndex
|
||||||
is LocalPropertyInformation.PrivateConstructorPairedProperty -> it.constructorSlot.parameterIndex
|
is LocalPropertyInformation.PrivateConstructorPairedProperty -> it.constructorSlot.parameterIndex
|
||||||
@ -317,7 +325,7 @@ internal data class LocalTypeInformationBuilder(val lookup: LocalTypeLookup,
|
|||||||
): List<LocalConstructorParameterInformation> {
|
): List<LocalConstructorParameterInformation> {
|
||||||
if (!constructorInformation.hasParameters) return emptyList()
|
if (!constructorInformation.hasParameters) return emptyList()
|
||||||
|
|
||||||
val indicesAddressedByProperties = properties.values.asSequence().mapNotNullTo(LinkedHashSet()) {
|
val indicesAddressedByProperties = properties.values.mapNotNullTo(LinkedHashSet()) {
|
||||||
when (it) {
|
when (it) {
|
||||||
is LocalPropertyInformation.ConstructorPairedProperty -> it.constructorSlot.parameterIndex
|
is LocalPropertyInformation.ConstructorPairedProperty -> it.constructorSlot.parameterIndex
|
||||||
is LocalPropertyInformation.PrivateConstructorPairedProperty -> it.constructorSlot.parameterIndex
|
is LocalPropertyInformation.PrivateConstructorPairedProperty -> it.constructorSlot.parameterIndex
|
||||||
@ -520,8 +528,7 @@ private fun constructorForDeserialization(type: Type): KFunction<Any>? {
|
|||||||
val defaultCtor = kotlinCtors.firstOrNull { it.parameters.isEmpty() }
|
val defaultCtor = kotlinCtors.firstOrNull { it.parameters.isEmpty() }
|
||||||
val nonDefaultCtors = kotlinCtors.filter { it != defaultCtor }
|
val nonDefaultCtors = kotlinCtors.filter { it != defaultCtor }
|
||||||
|
|
||||||
val preferredCandidate = clazz.kotlin.primaryConstructor ?:
|
val preferredCandidate = clazz.kotlin.primaryConstructor ?: when (nonDefaultCtors.size) {
|
||||||
when(nonDefaultCtors.size) {
|
|
||||||
1 -> nonDefaultCtors.first()
|
1 -> nonDefaultCtors.first()
|
||||||
0 -> defaultCtor
|
0 -> defaultCtor
|
||||||
else -> null
|
else -> null
|
||||||
@ -531,6 +538,19 @@ private fun constructorForDeserialization(type: Type): KFunction<Any>? {
|
|||||||
preferredCandidate.apply { isAccessible = true }
|
preferredCandidate.apply { isAccessible = true }
|
||||||
} catch (e: SecurityException) {
|
} catch (e: SecurityException) {
|
||||||
null
|
null
|
||||||
|
} catch (e: InaccessibleObjectException) {
|
||||||
|
if (!clazz.isJdkClass || preferredCandidate.visibility == KVisibility.PUBLIC) {
|
||||||
|
// We shouldn't be using private JDK constructors. For non-JDK classes, then re-throw as the client may need to open up that
|
||||||
|
// module to us. Also throw if we can't get access to a public JDK constructor, which can probably happen if the class is not
|
||||||
|
// exported (i.e. internal API).
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
with(loggerFor<LocalTypeInformationBuilder>()) {
|
||||||
|
if (isTraceEnabled) {
|
||||||
|
trace("Ignoring private JDK constructor", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ package net.corda.serialization.internal.amqp
|
|||||||
import net.corda.serialization.internal.amqp.testutils.TestSerializationOutput
|
import net.corda.serialization.internal.amqp.testutils.TestSerializationOutput
|
||||||
import net.corda.serialization.internal.amqp.testutils.deserialize
|
import net.corda.serialization.internal.amqp.testutils.deserialize
|
||||||
import net.corda.serialization.internal.amqp.testutils.testDefaultFactoryNoEvolution
|
import net.corda.serialization.internal.amqp.testutils.testDefaultFactoryNoEvolution
|
||||||
import org.assertj.core.api.Assertions
|
import org.assertj.core.api.Assertions.assertThatThrownBy
|
||||||
import org.assertj.core.api.Assertions.assertThatExceptionOfType
|
import org.assertj.core.api.Assertions.assertThatExceptionOfType
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.io.NotSerializableException
|
import java.io.NotSerializableException
|
||||||
@ -86,8 +86,9 @@ class DeserializeMapTests {
|
|||||||
val c = C(v)
|
val c = C(v)
|
||||||
|
|
||||||
// expected to throw
|
// expected to throw
|
||||||
Assertions.assertThatThrownBy { TestSerializationOutput(VERBOSE, sf).serialize(c) }
|
assertThatThrownBy { TestSerializationOutput(VERBOSE, sf).serialize(c) }
|
||||||
.isInstanceOf(IllegalArgumentException::class.java).hasMessageContaining("Unable to serialise deprecated type class java.util.Dictionary.")
|
.isInstanceOf(NotSerializableException::class.java)
|
||||||
|
.hasMessageContaining("Unable to serialise deprecated type class java.util.Dictionary.")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
@ -100,7 +101,7 @@ class DeserializeMapTests {
|
|||||||
val c = C(v)
|
val c = C(v)
|
||||||
|
|
||||||
// expected to throw
|
// expected to throw
|
||||||
Assertions.assertThatThrownBy { TestSerializationOutput(VERBOSE, sf).serialize(c) }
|
assertThatThrownBy { TestSerializationOutput(VERBOSE, sf).serialize(c) }
|
||||||
.isInstanceOf(IllegalArgumentException::class.java).hasMessageContaining("Unable to serialise deprecated type class java.util.Hashtable. Suggested fix: prefer java.util.map implementations")
|
.isInstanceOf(IllegalArgumentException::class.java).hasMessageContaining("Unable to serialise deprecated type class java.util.Hashtable. Suggested fix: prefer java.util.map implementations")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,7 +112,7 @@ class DeserializeMapTests {
|
|||||||
val c = C(HashMap(mapOf("A" to 1, "B" to 2)))
|
val c = C(HashMap(mapOf("A" to 1, "B" to 2)))
|
||||||
|
|
||||||
// expect this to throw
|
// expect this to throw
|
||||||
Assertions.assertThatThrownBy { TestSerializationOutput(VERBOSE, sf).serialize(c) }
|
assertThatThrownBy { TestSerializationOutput(VERBOSE, sf).serialize(c) }
|
||||||
.isInstanceOf(IllegalArgumentException::class.java).hasMessageContaining("Map type class java.util.HashMap is unstable under iteration. Suggested fix: use java.util.LinkedHashMap instead.")
|
.isInstanceOf(IllegalArgumentException::class.java).hasMessageContaining("Map type class java.util.HashMap is unstable under iteration. Suggested fix: use java.util.LinkedHashMap instead.")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,7 +122,7 @@ class DeserializeMapTests {
|
|||||||
|
|
||||||
val c = C(WeakHashMap(mapOf("A" to 1, "B" to 2)))
|
val c = C(WeakHashMap(mapOf("A" to 1, "B" to 2)))
|
||||||
|
|
||||||
Assertions.assertThatThrownBy { TestSerializationOutput(VERBOSE, sf).serialize(c) }
|
assertThatThrownBy { TestSerializationOutput(VERBOSE, sf).serialize(c) }
|
||||||
.isInstanceOf(IllegalArgumentException::class.java).hasMessageContaining("Weak references with map types not supported. Suggested fix: use java.util.LinkedHashMap instead.")
|
.isInstanceOf(IllegalArgumentException::class.java).hasMessageContaining("Weak references with map types not supported. Suggested fix: use java.util.LinkedHashMap instead.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,7 +55,6 @@ include 'client:jfx'
|
|||||||
include 'client:mock'
|
include 'client:mock'
|
||||||
include 'client:rpc'
|
include 'client:rpc'
|
||||||
include 'docker'
|
include 'docker'
|
||||||
include 'testing:client-rpc'
|
|
||||||
include 'testing:testserver'
|
include 'testing:testserver'
|
||||||
include 'testing:testserver:testcapsule:'
|
include 'testing:testserver:testcapsule:'
|
||||||
include 'experimental'
|
include 'experimental'
|
||||||
|
@ -1,73 +0,0 @@
|
|||||||
apply plugin: 'org.jetbrains.kotlin.jvm'
|
|
||||||
|
|
||||||
configurations {
|
|
||||||
smokeTestImplementation.extendsFrom compile
|
|
||||||
smokeTestRuntimeOnly.extendsFrom runtimeOnly
|
|
||||||
}
|
|
||||||
|
|
||||||
sourceSets {
|
|
||||||
smokeTest {
|
|
||||||
kotlin {
|
|
||||||
// We must NOT have any Node code on the classpath, so do NOT
|
|
||||||
// include the test or integrationTest dependencies here.
|
|
||||||
compileClasspath += main.output
|
|
||||||
runtimeClasspath += main.output
|
|
||||||
srcDir file('src/smoke-test/kotlin')
|
|
||||||
}
|
|
||||||
java {
|
|
||||||
compileClasspath += main.output
|
|
||||||
runtimeClasspath += main.output
|
|
||||||
srcDir file('src/smoke-test/java')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
processSmokeTestResources {
|
|
||||||
// Bring in the fully built corda.jar for use by NodeFactory in the smoke tests
|
|
||||||
from(project(":node:capsule").tasks['buildCordaJAR']) {
|
|
||||||
rename 'corda-(.*)', 'corda.jar'
|
|
||||||
}
|
|
||||||
from(project(':finance:workflows').tasks['jar']) {
|
|
||||||
rename '.*finance-workflows-.*', 'cordapp-finance-workflows.jar'
|
|
||||||
}
|
|
||||||
from(project(':finance:contracts').tasks['jar']) {
|
|
||||||
rename '.*finance-contracts-.*', 'cordapp-finance-contracts.jar'
|
|
||||||
}
|
|
||||||
from(project(':testing:cordapps:sleeping').tasks['jar']) {
|
|
||||||
rename 'testing-sleeping-cordapp-*', 'cordapp-sleeping.jar'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
// Smoke tests do NOT have any Node code on the classpath!
|
|
||||||
smokeTestImplementation project(':core')
|
|
||||||
smokeTestImplementation project(':client:rpc')
|
|
||||||
smokeTestImplementation project(':node-api')
|
|
||||||
smokeTestImplementation project(':smoke-test-utils')
|
|
||||||
smokeTestImplementation project(':finance:contracts')
|
|
||||||
smokeTestImplementation project(':finance:workflows')
|
|
||||||
smokeTestImplementation project(':testing:cordapps:sleeping')
|
|
||||||
smokeTestImplementation "io.reactivex:rxjava:$rxjava_version"
|
|
||||||
smokeTestImplementation "commons-io:commons-io:$commons_io_version"
|
|
||||||
smokeTestImplementation "org.hamcrest:hamcrest-library:2.1"
|
|
||||||
smokeTestImplementation "com.google.guava:guava-testlib:$guava_version"
|
|
||||||
smokeTestImplementation "org.apache.logging.log4j:log4j-slf4j-impl:$log4j_version"
|
|
||||||
smokeTestImplementation "org.apache.logging.log4j:log4j-core:$log4j_version"
|
|
||||||
smokeTestImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
|
||||||
smokeTestImplementation "org.assertj:assertj-core:${assertj_version}"
|
|
||||||
smokeTestImplementation "junit:junit:$junit_version"
|
|
||||||
|
|
||||||
smokeTestRuntimeOnly "org.junit.vintage:junit-vintage-engine:${junit_vintage_version}"
|
|
||||||
smokeTestRuntimeOnly "org.junit.platform:junit-platform-launcher:${junit_platform_version}"
|
|
||||||
|
|
||||||
// JDK11: required by Quasar at run-time
|
|
||||||
smokeTestRuntimeOnly "com.esotericsoftware:kryo:$kryo_version"
|
|
||||||
}
|
|
||||||
|
|
||||||
task smokeTest(type: Test) {
|
|
||||||
testClassesDirs = sourceSets.smokeTest.output.classesDirs
|
|
||||||
classpath = sourceSets.smokeTest.runtimeClasspath
|
|
||||||
|
|
||||||
jvmArgs test_add_opens
|
|
||||||
jvmArgs test_add_exports
|
|
||||||
}
|
|
@ -102,17 +102,20 @@ dependencies {
|
|||||||
compileJava {
|
compileJava {
|
||||||
doFirst {
|
doFirst {
|
||||||
options.compilerArgs = [
|
options.compilerArgs = [
|
||||||
'--add-exports', 'java.base/sun.nio.ch=ALL-UNNAMED'
|
'--add-modules', 'jdk.incubator.foreign'
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
processResources {
|
||||||
|
from(project(":node:capsule").files("src/main/resources/node-jvm-args.txt")) {
|
||||||
|
into("net/corda/testing/node/internal")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
task integrationTest(type: Test) {
|
task integrationTest(type: Test) {
|
||||||
testClassesDirs = sourceSets.integrationTest.output.classesDirs
|
testClassesDirs = sourceSets.integrationTest.output.classesDirs
|
||||||
classpath = sourceSets.integrationTest.runtimeClasspath
|
classpath = sourceSets.integrationTest.runtimeClasspath
|
||||||
|
|
||||||
jvmArgs test_add_opens
|
|
||||||
jvmArgs test_add_exports
|
|
||||||
}
|
}
|
||||||
|
|
||||||
jar {
|
jar {
|
||||||
|
@ -1,10 +1,14 @@
|
|||||||
package net.corda.testing.node
|
package net.corda.testing.node
|
||||||
|
|
||||||
|
import net.corda.core.internal.deleteRecursively
|
||||||
import net.corda.testing.common.internal.ProjectStructure.projectRootDir
|
import net.corda.testing.common.internal.ProjectStructure.projectRootDir
|
||||||
import net.corda.testing.node.internal.ProcessUtilities.startJavaProcess
|
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 org.junit.Test
|
||||||
|
import kotlin.io.path.Path
|
||||||
|
import kotlin.io.path.createDirectories
|
||||||
import kotlin.io.path.div
|
import kotlin.io.path.div
|
||||||
import kotlin.test.assertEquals
|
|
||||||
|
|
||||||
class MockNetworkIntegrationTests {
|
class MockNetworkIntegrationTests {
|
||||||
companion object {
|
companion object {
|
||||||
@ -22,16 +26,16 @@ class MockNetworkIntegrationTests {
|
|||||||
fun `does not leak non-daemon threads`() {
|
fun `does not leak non-daemon threads`() {
|
||||||
val quasar = projectRootDir / "lib" / "quasar.jar"
|
val quasar = projectRootDir / "lib" / "quasar.jar"
|
||||||
val quasarOptions = "m"
|
val quasarOptions = "m"
|
||||||
val moduleOpens = listOf(
|
|
||||||
"--add-opens", "java.base/java.time=ALL-UNNAMED", "--add-opens", "java.base/java.io=ALL-UNNAMED",
|
|
||||||
"--add-opens", "java.base/java.util=ALL-UNNAMED", "--add-opens", "java.base/java.net=ALL-UNNAMED",
|
|
||||||
"--add-opens", "java.base/java.nio=ALL-UNNAMED", "--add-opens", "java.base/java.lang.invoke=ALL-UNNAMED",
|
|
||||||
"--add-opens", "java.base/java.security.cert=ALL-UNNAMED", "--add-opens", "java.base/javax.net.ssl=ALL-UNNAMED",
|
|
||||||
"--add-opens", "java.base/java.util.concurrent=ALL-UNNAMED", "--add-opens", "java.sql/java.sql=ALL-UNNAMED",
|
|
||||||
"--add-opens", "java.base/java.lang=ALL-UNNAMED"
|
|
||||||
)
|
|
||||||
|
|
||||||
assertEquals(0, startJavaProcess<MockNetworkIntegrationTests>(emptyList(),
|
val workingDirectory = Path("build", "MockNetworkIntegrationTests").apply {
|
||||||
extraJvmArguments = listOf("-javaagent:$quasar=$quasarOptions") + moduleOpens).waitFor())
|
deleteRecursively()
|
||||||
|
createDirectories()
|
||||||
|
}
|
||||||
|
val process = startJavaProcess<MockNetworkIntegrationTests>(
|
||||||
|
emptyList(),
|
||||||
|
workingDirectory = workingDirectory,
|
||||||
|
extraJvmArguments = listOf("-javaagent:$quasar=$quasarOptions") + nodeJvmArgs
|
||||||
|
)
|
||||||
|
assertThat(process.waitFor()).isZero()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,6 @@ import java.io.BufferedReader
|
|||||||
import java.io.InputStreamReader
|
import java.io.InputStreamReader
|
||||||
import java.util.stream.Collectors
|
import java.util.stream.Collectors
|
||||||
|
|
||||||
|
|
||||||
@RunWith(value = Parameterized::class)
|
@RunWith(value = Parameterized::class)
|
||||||
class CordaCliWrapperErrorHandlingTests(val arguments: List<String>, val outputRegexPattern: String) {
|
class CordaCliWrapperErrorHandlingTests(val arguments: List<String>, val outputRegexPattern: String) {
|
||||||
|
|
||||||
@ -31,10 +30,7 @@ class CordaCliWrapperErrorHandlingTests(val arguments: List<String>, val outputR
|
|||||||
|
|
||||||
@Test(timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
fun `Run CordaCliWrapper sample app with arguments and check error output matches regExp`() {
|
fun `Run CordaCliWrapper sample app with arguments and check error output matches regExp`() {
|
||||||
val process = ProcessUtilities.startJavaProcess(
|
val process = ProcessUtilities.startJavaProcess(className = className, arguments = arguments)
|
||||||
className = className,
|
|
||||||
arguments = arguments,
|
|
||||||
inheritIO = false)
|
|
||||||
|
|
||||||
process.waitFor()
|
process.waitFor()
|
||||||
|
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
package net.corda.testing.node.internal
|
package net.corda.testing.node.internal
|
||||||
|
|
||||||
|
import net.corda.core.internal.deleteRecursively
|
||||||
import net.corda.testing.common.internal.ProjectStructure.projectRootDir
|
import net.corda.testing.common.internal.ProjectStructure.projectRootDir
|
||||||
import net.corda.testing.node.internal.ProcessUtilities.startJavaProcess
|
import net.corda.testing.node.internal.ProcessUtilities.startJavaProcess
|
||||||
|
import org.assertj.core.api.Assertions.assertThat
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
import kotlin.io.path.Path
|
||||||
|
import kotlin.io.path.createDirectories
|
||||||
import kotlin.io.path.div
|
import kotlin.io.path.div
|
||||||
import kotlin.test.assertEquals
|
|
||||||
|
|
||||||
class InternalMockNetworkIntegrationTests {
|
class InternalMockNetworkIntegrationTests {
|
||||||
companion object {
|
companion object {
|
||||||
@ -22,17 +25,16 @@ class InternalMockNetworkIntegrationTests {
|
|||||||
fun `does not leak non-daemon threads`() {
|
fun `does not leak non-daemon threads`() {
|
||||||
val quasar = projectRootDir / "lib" / "quasar.jar"
|
val quasar = projectRootDir / "lib" / "quasar.jar"
|
||||||
val quasarOptions = "m"
|
val quasarOptions = "m"
|
||||||
val moduleOpens = listOf(
|
|
||||||
"--add-opens", "java.base/java.time=ALL-UNNAMED", "--add-opens", "java.base/java.io=ALL-UNNAMED",
|
|
||||||
"--add-opens", "java.base/java.util=ALL-UNNAMED", "--add-opens", "java.base/java.net=ALL-UNNAMED",
|
|
||||||
"--add-opens", "java.base/java.nio=ALL-UNNAMED", "--add-opens", "java.base/java.lang.invoke=ALL-UNNAMED",
|
|
||||||
"--add-opens", "java.base/java.security.cert=ALL-UNNAMED", "--add-opens", "java.base/javax.net.ssl=ALL-UNNAMED",
|
|
||||||
"--add-opens", "java.base/java.util.concurrent=ALL-UNNAMED", "--add-opens", "java.sql/java.sql=ALL-UNNAMED",
|
|
||||||
"--add-opens", "java.base/java.lang=ALL-UNNAMED"
|
|
||||||
)
|
|
||||||
|
|
||||||
assertEquals(0, startJavaProcess<InternalMockNetworkIntegrationTests>(emptyList(),
|
val workingDirectory = Path("build", "InternalMockNetworkIntegrationTests").apply {
|
||||||
extraJvmArguments = listOf("-javaagent:$quasar=$quasarOptions") + moduleOpens
|
deleteRecursively()
|
||||||
).waitFor())
|
createDirectories()
|
||||||
|
}
|
||||||
|
val process = startJavaProcess<InternalMockNetworkIntegrationTests>(
|
||||||
|
emptyList(),
|
||||||
|
workingDirectory = workingDirectory,
|
||||||
|
extraJvmArguments = listOf("-javaagent:$quasar=$quasarOptions") + nodeJvmArgs
|
||||||
|
)
|
||||||
|
assertThat(process.waitFor()).isZero()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,97 +1,108 @@
|
|||||||
package net.corda.testing.driver;
|
package net.corda.testing.driver;
|
||||||
|
|
||||||
import sun.misc.Unsafe;
|
import jdk.incubator.foreign.MemoryHandles;
|
||||||
import sun.nio.ch.DirectBuffer;
|
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.IOException;
|
||||||
import java.io.RandomAccessFile;
|
import java.io.UncheckedIOException;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.invoke.VarHandle;
|
||||||
import java.net.ServerSocket;
|
import java.net.ServerSocket;
|
||||||
|
import java.nio.ByteOrder;
|
||||||
import java.nio.MappedByteBuffer;
|
import java.nio.MappedByteBuffer;
|
||||||
import java.nio.channels.FileChannel;
|
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;
|
||||||
|
|
||||||
/**
|
import static java.nio.file.StandardOpenOption.READ;
|
||||||
* JDK11 upgrade: rewritten in Java to gain access to private internal JDK classes via module directives (not available to Kotlin compiler):
|
import static java.nio.file.StandardOpenOption.WRITE;
|
||||||
* import sun.misc.Unsafe;
|
|
||||||
* import sun.nio.ch.DirectBuffer;
|
// 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 {
|
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;
|
private final int startPort;
|
||||||
static private final int FIRST_EPHEMERAL_PORT = 30_000;
|
private final int endPort;
|
||||||
|
|
||||||
private int startPort;
|
private final MemorySegment memorySegment;
|
||||||
private int endPort;
|
private final VarHandle intHandle;
|
||||||
|
private final MappedByteBuffer unsafeBuffer;
|
||||||
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 SharedMemoryIncremental(int startPort, int endPort) {
|
private SharedMemoryIncremental(int startPort, int endPort) {
|
||||||
this.startPort = startPort;
|
this.startPort = startPort;
|
||||||
this.endPort = endPort;
|
this.endPort = endPort;
|
||||||
|
Path file = Path.of(System.getProperty("user.home"), "corda-" + startPort + "-to-" + endPort + "-port-allocator.bin");
|
||||||
try {
|
try {
|
||||||
mb = backingFile.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, 16);
|
try {
|
||||||
startingAddress = ((DirectBuffer) mb).address();
|
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) {
|
} 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);
|
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
|
@Override
|
||||||
public int nextPort() {
|
public int nextPort() {
|
||||||
long oldValue;
|
while (true) {
|
||||||
long newValue;
|
int oldValue;
|
||||||
boolean loopSuccess;
|
if (intHandle != null) {
|
||||||
do {
|
oldValue = (int) intHandle.getVolatile(memorySegment, 0L);
|
||||||
oldValue = UNSAFE.getLongVolatile(null, startingAddress);
|
} else {
|
||||||
|
oldValue = unsafeBuffer.getInt(0);
|
||||||
|
}
|
||||||
|
int newValue;
|
||||||
if (oldValue + 1 >= endPort || oldValue < startPort) {
|
if (oldValue + 1 >= endPort || oldValue < startPort) {
|
||||||
newValue = startPort;
|
newValue = startPort;
|
||||||
} else {
|
} else {
|
||||||
newValue = (oldValue + 1);
|
newValue = (oldValue + 1);
|
||||||
}
|
}
|
||||||
boolean reserveSuccess = UNSAFE.compareAndSwapLong(null, startingAddress, oldValue, newValue);
|
if (intHandle != null) {
|
||||||
loopSuccess = reserveSuccess && isLocalPortAvailable(newValue);
|
if (!intHandle.compareAndSet(memorySegment, 0L, oldValue, newValue)) {
|
||||||
} while (!loopSuccess);
|
continue;
|
||||||
|
}
|
||||||
return (int) newValue;
|
} else {
|
||||||
|
unsafeBuffer.putInt(0, newValue);
|
||||||
|
}
|
||||||
|
if (isLocalPortAvailable(newValue)) {
|
||||||
|
return newValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isLocalPortAvailable(int portToTest) {
|
||||||
private boolean isLocalPortAvailable(Long portToTest) {
|
try (ServerSocket ignored = new ServerSocket(portToTest)) {
|
||||||
try (ServerSocket serverSocket = new ServerSocket(Math.toIntExact(portToTest))) {
|
return true;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
// Don't catch anything other than IOException here in case we
|
// Don't catch anything other than IOException here in case we
|
||||||
// accidentally create an infinite loop. For example, installing
|
// accidentally create an infinite loop. For example, installing
|
||||||
// a SecurityManager could throw AccessControlException.
|
// a SecurityManager could throw AccessControlException.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -49,6 +49,7 @@ import net.corda.core.serialization.SerializeAsToken
|
|||||||
import net.corda.core.serialization.internal.AttachmentsClassLoaderCacheImpl
|
import net.corda.core.serialization.internal.AttachmentsClassLoaderCacheImpl
|
||||||
import net.corda.core.transactions.SignedTransaction
|
import net.corda.core.transactions.SignedTransaction
|
||||||
import net.corda.core.utilities.NetworkHostAndPort
|
import net.corda.core.utilities.NetworkHostAndPort
|
||||||
|
import net.corda.core.utilities.loggerFor
|
||||||
import net.corda.coretesting.internal.DEV_ROOT_CA
|
import net.corda.coretesting.internal.DEV_ROOT_CA
|
||||||
import net.corda.node.VersionInfo
|
import net.corda.node.VersionInfo
|
||||||
import net.corda.node.internal.cordapp.JarScanningCordappLoader
|
import net.corda.node.internal.cordapp.JarScanningCordappLoader
|
||||||
@ -76,7 +77,6 @@ import net.corda.testing.internal.MockCordappProvider
|
|||||||
import net.corda.testing.internal.TestingNamedCacheFactory
|
import net.corda.testing.internal.TestingNamedCacheFactory
|
||||||
import net.corda.testing.internal.configureDatabase
|
import net.corda.testing.internal.configureDatabase
|
||||||
import net.corda.testing.internal.services.InternalMockAttachmentStorage
|
import net.corda.testing.internal.services.InternalMockAttachmentStorage
|
||||||
import net.corda.testing.node.internal.DriverDSLImpl
|
|
||||||
import net.corda.testing.node.internal.MockCryptoService
|
import net.corda.testing.node.internal.MockCryptoService
|
||||||
import net.corda.testing.node.internal.MockKeyManagementService
|
import net.corda.testing.node.internal.MockKeyManagementService
|
||||||
import net.corda.testing.node.internal.MockNetworkParametersStorage
|
import net.corda.testing.node.internal.MockNetworkParametersStorage
|
||||||
@ -85,6 +85,7 @@ import net.corda.testing.node.internal.cordappsForPackages
|
|||||||
import net.corda.testing.node.internal.getCallerPackage
|
import net.corda.testing.node.internal.getCallerPackage
|
||||||
import net.corda.testing.services.MockAttachmentStorage
|
import net.corda.testing.services.MockAttachmentStorage
|
||||||
import java.io.ByteArrayOutputStream
|
import java.io.ByteArrayOutputStream
|
||||||
|
import java.nio.file.FileAlreadyExistsException
|
||||||
import java.nio.file.Paths
|
import java.nio.file.Paths
|
||||||
import java.security.KeyPair
|
import java.security.KeyPair
|
||||||
import java.sql.Connection
|
import java.sql.Connection
|
||||||
@ -141,8 +142,8 @@ open class MockServices private constructor(
|
|||||||
val dbPath = dbDir.resolve("persistence")
|
val dbPath = dbDir.resolve("persistence")
|
||||||
try {
|
try {
|
||||||
DatabaseSnapshot.copyDatabaseSnapshot(dbDir)
|
DatabaseSnapshot.copyDatabaseSnapshot(dbDir)
|
||||||
} catch (ex: java.nio.file.FileAlreadyExistsException) {
|
} catch (e: FileAlreadyExistsException) {
|
||||||
DriverDSLImpl.log.warn("Database already exists on disk, not attempting to pre-migrate database.")
|
loggerFor<MockServices>().warn("Database already exists on disk, not attempting to pre-migrate database.")
|
||||||
}
|
}
|
||||||
val props = Properties()
|
val props = Properties()
|
||||||
props.setProperty("dataSourceClassName", "org.h2.jdbcx.JdbcDataSource")
|
props.setProperty("dataSourceClassName", "org.h2.jdbcx.JdbcDataSource")
|
||||||
|
@ -45,6 +45,7 @@ import net.corda.core.utilities.NetworkHostAndPort
|
|||||||
import net.corda.core.utilities.contextLogger
|
import net.corda.core.utilities.contextLogger
|
||||||
import net.corda.core.utilities.debug
|
import net.corda.core.utilities.debug
|
||||||
import net.corda.core.utilities.getOrThrow
|
import net.corda.core.utilities.getOrThrow
|
||||||
|
import net.corda.core.utilities.loggerFor
|
||||||
import net.corda.core.utilities.millis
|
import net.corda.core.utilities.millis
|
||||||
import net.corda.core.utilities.toHexString
|
import net.corda.core.utilities.toHexString
|
||||||
import net.corda.coretesting.internal.stubs.CertificateStoreStubs
|
import net.corda.coretesting.internal.stubs.CertificateStoreStubs
|
||||||
@ -845,7 +846,7 @@ class DriverDSLImpl(
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val RPC_CONNECT_POLL_INTERVAL: Duration = 100.millis
|
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
|
// While starting with inProcess mode, we need to have different names to avoid clashes
|
||||||
private val inMemoryCounter = AtomicInteger()
|
private val inMemoryCounter = AtomicInteger()
|
||||||
@ -960,7 +961,7 @@ class DriverDSLImpl(
|
|||||||
"org.hamcrest**;org.hibernate**;org.jboss**;org.jcp**;org.joda**;org.junit**;org.mockito**;org.objectweb**;" +
|
"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**;" +
|
"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**;)"
|
"com.lmax**;picocli**;liquibase**;com.github.benmanes**;org.json**;org.postgresql**;nonapi.io.github.classgraph**;)"
|
||||||
val excludeClassloaderPattern = "l(net.corda.djvm.**;net.corda.core.serialization.internal.**)"
|
val excludeClassloaderPattern = "l(net.corda.core.serialization.internal.**)"
|
||||||
val quasarOptions = "m"
|
val quasarOptions = "m"
|
||||||
val extraJvmArguments = systemProperties.removeResolvedClasspath().map { "-D${it.key}=${it.value}" } +
|
val extraJvmArguments = systemProperties.removeResolvedClasspath().map { "-D${it.key}=${it.value}" } +
|
||||||
"-javaagent:$quasarJarPath=$quasarOptions$excludePackagePattern$excludeClassloaderPattern"
|
"-javaagent:$quasarJarPath=$quasarOptions$excludePackagePattern$excludeClassloaderPattern"
|
||||||
@ -1002,24 +1003,11 @@ class DriverDSLImpl(
|
|||||||
&& !cpPathEntry.isExcludedJar
|
&& !cpPathEntry.isExcludedJar
|
||||||
}
|
}
|
||||||
|
|
||||||
val moduleOpens = listOf(
|
|
||||||
"--add-opens", "java.base/java.time=ALL-UNNAMED", "--add-opens", "java.base/java.io=ALL-UNNAMED",
|
|
||||||
"--add-opens", "java.base/java.util=ALL-UNNAMED", "--add-opens", "java.base/java.net=ALL-UNNAMED",
|
|
||||||
"--add-opens", "java.base/java.nio=ALL-UNNAMED", "--add-opens", "java.base/java.lang.invoke=ALL-UNNAMED",
|
|
||||||
"--add-opens", "java.base/java.security.cert=ALL-UNNAMED", "--add-opens", "java.base/javax.net.ssl=ALL-UNNAMED",
|
|
||||||
"--add-opens", "java.base/java.util.concurrent=ALL-UNNAMED", "--add-opens", "java.sql/java.sql=ALL-UNNAMED",
|
|
||||||
"--add-opens", "java.base/java.lang=ALL-UNNAMED"
|
|
||||||
)
|
|
||||||
|
|
||||||
val moduleExports = listOf(
|
|
||||||
"--add-exports", "java.base/sun.nio.ch=ALL-UNNAMED"
|
|
||||||
)
|
|
||||||
|
|
||||||
return ProcessUtilities.startJavaProcess(
|
return ProcessUtilities.startJavaProcess(
|
||||||
className = "net.corda.node.Corda", // cannot directly get class for this, so just use string
|
className = "net.corda.node.Corda", // cannot directly get class for this, so just use string
|
||||||
arguments = arguments,
|
arguments = arguments,
|
||||||
jdwpPort = debugPort,
|
jdwpPort = debugPort,
|
||||||
extraJvmArguments = extraJvmArguments + bytemanJvmArgs + moduleOpens + moduleExports + "-Dnet.corda.node.printErrorsToStdErr=true",
|
extraJvmArguments = extraJvmArguments + bytemanJvmArgs + nodeJvmArgs + "-Dnet.corda.node.printErrorsToStdErr=true",
|
||||||
workingDirectory = config.corda.baseDirectory,
|
workingDirectory = config.corda.baseDirectory,
|
||||||
maximumHeapSize = maximumHeapSize,
|
maximumHeapSize = maximumHeapSize,
|
||||||
classPath = cp,
|
classPath = cp,
|
||||||
@ -1066,22 +1054,13 @@ class DriverDSLImpl(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun startWebserver(handle: NodeHandleInternal, debugPort: Int?, maximumHeapSize: String): Process {
|
private fun startWebserver(handle: NodeHandleInternal, debugPort: Int?, maximumHeapSize: String): Process {
|
||||||
val className = "net.corda.webserver.WebServer"
|
|
||||||
val moduleOpens = listOf(
|
|
||||||
"--add-opens", "java.base/java.time=ALL-UNNAMED", "--add-opens", "java.base/java.io=ALL-UNNAMED",
|
|
||||||
"--add-opens", "java.base/java.util=ALL-UNNAMED", "--add-opens", "java.base/java.net=ALL-UNNAMED",
|
|
||||||
"--add-opens", "java.base/java.nio=ALL-UNNAMED", "--add-opens", "java.base/java.lang.invoke=ALL-UNNAMED",
|
|
||||||
"--add-opens", "java.base/java.security.cert=ALL-UNNAMED", "--add-opens", "java.base/javax.net.ssl=ALL-UNNAMED",
|
|
||||||
"--add-opens", "java.base/java.util.concurrent=ALL-UNNAMED", "--add-opens", "java.sql/java.sql=ALL-UNNAMED",
|
|
||||||
"--add-opens", "java.base/java.lang=ALL-UNNAMED"
|
|
||||||
)
|
|
||||||
|
|
||||||
writeConfig(handle.baseDirectory, "web-server.conf", handle.toWebServerConfig())
|
writeConfig(handle.baseDirectory, "web-server.conf", handle.toWebServerConfig())
|
||||||
return ProcessUtilities.startJavaProcess(
|
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()),
|
arguments = listOf(BASE_DIR, handle.baseDirectory.toString()),
|
||||||
jdwpPort = debugPort,
|
jdwpPort = debugPort,
|
||||||
extraJvmArguments = listOf("-Dname=node-${handle.p2pAddress}-webserver") + moduleOpens +
|
extraJvmArguments = listOf("-Dname=node-${handle.p2pAddress}-webserver") +
|
||||||
inheritFromParentProcess().map { "-D${it.first}=${it.second}" },
|
inheritFromParentProcess().map { "-D${it.first}=${it.second}" },
|
||||||
maximumHeapSize = maximumHeapSize
|
maximumHeapSize = maximumHeapSize
|
||||||
)
|
)
|
||||||
@ -1101,12 +1080,11 @@ class DriverDSLImpl(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun NodeHandleInternal.toWebServerConfig(): Config {
|
private fun NodeHandleInternal.toWebServerConfig(): Config {
|
||||||
|
|
||||||
var config = ConfigFactory.empty()
|
var config = ConfigFactory.empty()
|
||||||
config += "webAddress" to webAddress.toString()
|
config += "webAddress" to webAddress.toString()
|
||||||
config += "myLegalName" to configuration.myLegalName.toString()
|
config += "myLegalName" to configuration.myLegalName.toString()
|
||||||
config += "rpcAddress" to configuration.rpcOptions.address.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 += "useHTTPS" to useHTTPS
|
||||||
config += "baseDirectory" to configuration.baseDirectory.toAbsolutePath().toString()
|
config += "baseDirectory" to configuration.baseDirectory.toAbsolutePath().toString()
|
||||||
|
|
||||||
@ -1276,7 +1254,7 @@ fun <DI : DriverDSL, D : InternalDriverDSL, A> genericDriver(
|
|||||||
driverDsl.start()
|
driverDsl.start()
|
||||||
return dsl(coerce(driverDsl))
|
return dsl(coerce(driverDsl))
|
||||||
} catch (exception: Throwable) {
|
} 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
|
throw exception
|
||||||
} finally {
|
} finally {
|
||||||
driverDsl.shutdown()
|
driverDsl.shutdown()
|
||||||
|
@ -301,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.
|
* Should only be used by Driver and MockNode.
|
||||||
*/
|
*/
|
||||||
|
@ -39,7 +39,6 @@ object ProcessUtilities {
|
|||||||
maximumHeapSize: String? = null,
|
maximumHeapSize: String? = null,
|
||||||
identifier: String = "",
|
identifier: String = "",
|
||||||
environmentVariables: Map<String,String> = emptyMap(),
|
environmentVariables: Map<String,String> = emptyMap(),
|
||||||
inheritIO: Boolean = true
|
|
||||||
): Process {
|
): Process {
|
||||||
val command = mutableListOf<String>().apply {
|
val command = mutableListOf<String>().apply {
|
||||||
add(javaPath)
|
add(javaPath)
|
||||||
@ -50,7 +49,6 @@ object ProcessUtilities {
|
|||||||
addAll(arguments)
|
addAll(arguments)
|
||||||
}
|
}
|
||||||
return ProcessBuilder(command).apply {
|
return ProcessBuilder(command).apply {
|
||||||
if (inheritIO) inheritIO()
|
|
||||||
environment().putAll(environmentVariables)
|
environment().putAll(environmentVariables)
|
||||||
environment()["CLASSPATH"] = classPath.joinToString(File.pathSeparator)
|
environment()["CLASSPATH"] = classPath.joinToString(File.pathSeparator)
|
||||||
if (workingDirectory != null) {
|
if (workingDirectory != null) {
|
||||||
|
@ -148,24 +148,25 @@ class NodeProcess(
|
|||||||
|
|
||||||
|
|
||||||
private fun createSchema(nodeDir: Path){
|
private fun createSchema(nodeDir: Path){
|
||||||
val process = startNode(nodeDir, arrayOf("run-migration-scripts", "--core-schemas", "--app-schemas"))
|
val process = startNode(nodeDir, "run-migration-scripts", "--core-schemas", "--app-schemas")
|
||||||
if (!process.waitFor(schemaCreationTimeOutSeconds, SECONDS)) {
|
if (!process.waitFor(schemaCreationTimeOutSeconds, SECONDS)) {
|
||||||
process.destroy()
|
process.destroy()
|
||||||
throw SchemaCreationTimedOutError(nodeDir)
|
throw SchemaCreationTimedOutError(nodeDir)
|
||||||
}
|
}
|
||||||
if (process.exitValue() != 0){
|
if (process.exitValue() != 0) {
|
||||||
throw SchemaCreationFailedError(nodeDir)
|
throw SchemaCreationFailedError(nodeDir)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("SpreadOperator")
|
private fun startNode(nodeDir: Path, vararg extraArgs: String): Process {
|
||||||
private fun startNode(nodeDir: Path, extraArgs: Array<String> = emptyArray()): Process {
|
val command = arrayListOf(javaPath.toString(), "-Dcapsule.log=verbose", "-jar", cordaJar.toString())
|
||||||
|
command += extraArgs
|
||||||
|
val now = formatter.format(Instant.now())
|
||||||
val builder = ProcessBuilder()
|
val builder = ProcessBuilder()
|
||||||
.command(javaPath.toString(), "-Dcapsule.log=verbose", "-jar", cordaJar.toString(), *extraArgs)
|
.command(command)
|
||||||
.directory(nodeDir.toFile())
|
.directory(nodeDir.toFile())
|
||||||
.redirectError(ProcessBuilder.Redirect.INHERIT)
|
.redirectError((nodeDir / "$now-stderr.log").toFile())
|
||||||
.redirectOutput(ProcessBuilder.Redirect.INHERIT)
|
.redirectOutput((nodeDir / "$now-stdout.log").toFile())
|
||||||
|
|
||||||
builder.environment().putAll(mapOf(
|
builder.environment().putAll(mapOf(
|
||||||
"CAPSULE_CACHE_DIR" to (buildDirectory / "capsule").toString()
|
"CAPSULE_CACHE_DIR" to (buildDirectory / "capsule").toString()
|
||||||
))
|
))
|
||||||
|
@ -80,9 +80,6 @@ dependencies {
|
|||||||
tasks.register('integrationTest', Test) {
|
tasks.register('integrationTest', Test) {
|
||||||
testClassesDirs = sourceSets.integrationTest.output.classesDirs
|
testClassesDirs = sourceSets.integrationTest.output.classesDirs
|
||||||
classpath = sourceSets.integrationTest.runtimeClasspath
|
classpath = sourceSets.integrationTest.runtimeClasspath
|
||||||
|
|
||||||
jvmArgs test_add_opens
|
|
||||||
jvmArgs test_add_exports
|
|
||||||
}
|
}
|
||||||
|
|
||||||
jar {
|
jar {
|
||||||
|
@ -2,14 +2,22 @@
|
|||||||
// must also be in the default package. When using Kotlin there are a whole host of exceptions
|
// must also be in the default package. When using Kotlin there are a whole host of exceptions
|
||||||
// trying to construct this from Capsule, so it is written in Java.
|
// trying to construct this from Capsule, so it is written in Java.
|
||||||
|
|
||||||
import com.typesafe.config.*;
|
import com.typesafe.config.Config;
|
||||||
|
import com.typesafe.config.ConfigException;
|
||||||
|
import com.typesafe.config.ConfigFactory;
|
||||||
|
import com.typesafe.config.ConfigParseOptions;
|
||||||
|
import com.typesafe.config.ConfigValue;
|
||||||
import sun.misc.Signal;
|
import sun.misc.Signal;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.*;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
public class CordaWebserverCaplet extends Capsule {
|
public class CordaWebserverCaplet extends Capsule {
|
||||||
@ -37,11 +45,6 @@ public class CordaWebserverCaplet extends Capsule {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
File getConfigFile(List<String> args, String baseDir) {
|
|
||||||
String config = getOptionMultiple(args, Arrays.asList("--config-file", "-f"));
|
|
||||||
return (config == null || config.equals("")) ? new File(baseDir, "node.conf") : new File(config);
|
|
||||||
}
|
|
||||||
|
|
||||||
String getBaseDirectory(List<String> args) {
|
String getBaseDirectory(List<String> args) {
|
||||||
String baseDir = getOptionMultiple(args, Arrays.asList("--base-directory", "-b"));
|
String baseDir = getOptionMultiple(args, Arrays.asList("--base-directory", "-b"));
|
||||||
return Paths.get((baseDir == null) ? "." : baseDir).toAbsolutePath().normalize().toString();
|
return Paths.get((baseDir == null) ? "." : baseDir).toAbsolutePath().normalize().toString();
|
||||||
@ -69,7 +72,7 @@ public class CordaWebserverCaplet extends Capsule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (arg.toLowerCase().startsWith(lowerCaseOption)) {
|
if (arg.toLowerCase().startsWith(lowerCaseOption)) {
|
||||||
if (arg.length() > option.length() && arg.substring(option.length(), option.length() + 1).equals("=")) {
|
if (arg.length() > option.length() && arg.charAt(option.length()) == '=') {
|
||||||
return arg.substring(option.length() + 1);
|
return arg.substring(option.length() + 1);
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
@ -87,23 +90,6 @@ public class CordaWebserverCaplet extends Capsule {
|
|||||||
return super.prelaunch(jvmArgs, args);
|
return super.prelaunch(jvmArgs, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Capsule does not handle multiple instances of same option hence we add in the args here to process builder
|
|
||||||
// For multiple instances Capsule jvm args handling works on basis that one overrides the other.
|
|
||||||
@Override
|
|
||||||
protected int launch(ProcessBuilder pb) throws IOException, InterruptedException {
|
|
||||||
if (isAtLeastJavaVersion11()) {
|
|
||||||
List<String> args = pb.command();
|
|
||||||
List<String> myArgs = Arrays.asList(
|
|
||||||
"--add-opens=java.base/java.lang=ALL-UNNAMED",
|
|
||||||
"--add-opens=java.base/java.time=ALL-UNNAMED",
|
|
||||||
"--add-opens=java.base/java.io=ALL-UNNAMED",
|
|
||||||
"--add-opens=java.base/java.nio=ALL-UNNAMED");
|
|
||||||
args.addAll(1, myArgs);
|
|
||||||
pb.command(args);
|
|
||||||
}
|
|
||||||
return super.launch(pb);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add working directory variable to capsules string replacement variables.
|
// Add working directory variable to capsules string replacement variables.
|
||||||
@Override
|
@Override
|
||||||
protected String getVarValue(String var) {
|
protected String getVarValue(String var) {
|
||||||
@ -157,9 +143,6 @@ public class CordaWebserverCaplet extends Capsule {
|
|||||||
} catch (ConfigException e) {
|
} catch (ConfigException e) {
|
||||||
log(LOG_QUIET, e);
|
log(LOG_QUIET, e);
|
||||||
}
|
}
|
||||||
if (isAtLeastJavaVersion11()) {
|
|
||||||
jvmArgs.add("-Dnashorn.args=--no-deprecation-warning");
|
|
||||||
}
|
|
||||||
return (T) jvmArgs;
|
return (T) jvmArgs;
|
||||||
} else if (ATTR_SYSTEM_PROPERTIES == attr) {
|
} else if (ATTR_SYSTEM_PROPERTIES == attr) {
|
||||||
// Add system properties, if specified, from the config.
|
// Add system properties, if specified, from the config.
|
||||||
@ -202,14 +185,6 @@ public class CordaWebserverCaplet extends Capsule {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isAtLeastJavaVersion11() {
|
|
||||||
String version = System.getProperty("java.specification.version");
|
|
||||||
if (version != null) {
|
|
||||||
return Float.parseFloat(version) >= 11f;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Boolean checkIfCordappDirExists(File dir) {
|
private Boolean checkIfCordappDirExists(File dir) {
|
||||||
try {
|
try {
|
||||||
if (!dir.mkdir() && !dir.exists()) { // It is unlikely to enter this if-branch, but just in case.
|
if (!dir.mkdir() && !dir.exists()) { // It is unlikely to enter this if-branch, but just in case.
|
||||||
|
@ -55,10 +55,6 @@ tasks.register('buildWebserverJar', FatCapsule) {
|
|||||||
// If you change these flags, please also update Driver.kt
|
// If you change these flags, please also update Driver.kt
|
||||||
jvmArgs = ['-Xmx200m']
|
jvmArgs = ['-Xmx200m']
|
||||||
}
|
}
|
||||||
|
|
||||||
manifest {
|
|
||||||
attributes('Add-Opens': 'java.management/com.sun.jmx.mbeanserver java.base/java.lang')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
artifacts {
|
artifacts {
|
||||||
|
@ -16,20 +16,3 @@ dependencies {
|
|||||||
|
|
||||||
runtimeOnly "org.apache.logging.log4j:log4j-slf4j-impl:$log4j_version"
|
runtimeOnly "org.apache.logging.log4j:log4j-slf4j-impl:$log4j_version"
|
||||||
}
|
}
|
||||||
|
|
||||||
jar {
|
|
||||||
manifest {
|
|
||||||
attributes("Add-Opens":
|
|
||||||
"java.base/java.lang " +
|
|
||||||
"java.base/java.lang.reflect " +
|
|
||||||
"java.base/java.lang.invoke " +
|
|
||||||
"java.base/java.util " +
|
|
||||||
"java.base/java.time " +
|
|
||||||
"java.base/java.io " +
|
|
||||||
"java.base/java.net " +
|
|
||||||
"java.base/javax.net.ssl " +
|
|
||||||
"java.base/java.security.cert " +
|
|
||||||
"java.base/java.nio"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Loading…
Reference in New Issue
Block a user