mirror of
https://github.com/corda/corda.git
synced 2025-03-17 17:45:17 +00:00
Merge pull request #804 from corda/mnesbit-prune-bridge-dependencies
ENT-1847: Slim down bridge capsule jar
This commit is contained in:
commit
ea2f9c1ef8
@ -10,6 +10,7 @@
|
||||
* This build.gradle exists to publish our capsule (executable fat jar) to maven. It cannot be placed in the
|
||||
* bridges project because the bintray plugin cannot publish two modules from one project.
|
||||
*/
|
||||
apply plugin: 'kotlin'
|
||||
apply plugin: 'net.corda.plugins.publish-utils'
|
||||
apply plugin: 'us.kirchmeier.capsule'
|
||||
apply plugin: 'com.jfrog.artifactory'
|
||||
@ -19,11 +20,48 @@ description 'Corda bridge server capsule'
|
||||
configurations {
|
||||
runtimeArtifacts
|
||||
capsuleRuntime
|
||||
|
||||
smokeTestCompile.extendsFrom testCompile
|
||||
smokeTestRuntime.extendsFrom testRuntime
|
||||
}
|
||||
|
||||
compileTestKotlin {
|
||||
kotlinOptions.jvmTarget = "1.8"
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
smokeTest {
|
||||
kotlin {
|
||||
// We must NOT have any Bridge 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')
|
||||
}
|
||||
resources {
|
||||
srcDir file('src/smoke-test/resources')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// TypeSafe Config: for simple and human friendly config files.
|
||||
capsuleRuntime "com.typesafe:config:$typesafe_config_version"
|
||||
|
||||
|
||||
// Smoke tests do NOT have any Node code on the classpath!
|
||||
smokeTestCompile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||
smokeTestCompile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
|
||||
smokeTestCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
||||
smokeTestCompile "org.apache.logging.log4j:log4j-slf4j-impl:$log4j_version"
|
||||
smokeTestCompile "org.apache.logging.log4j:log4j-core:$log4j_version"
|
||||
smokeTestCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
||||
smokeTestCompile "org.assertj:assertj-core:${assertj_version}"
|
||||
smokeTestCompile project(':node-driver')
|
||||
smokeTestCompile project(':test-utils')
|
||||
smokeTestCompile "org.apache.curator:curator-test:${curator_version}"
|
||||
smokeTestCompile "junit:junit:$junit_version"
|
||||
|
||||
}
|
||||
|
||||
// Force the Caplet to target Java 6. This ensures that running 'java -jar corda.jar' on any Java 6 VM upwards
|
||||
@ -65,6 +103,19 @@ task buildBridgeServerJar(type: FatCapsule, dependsOn: project(':bridge').jar) {
|
||||
}
|
||||
}
|
||||
|
||||
processSmokeTestResources {
|
||||
|
||||
from(project.tasks['buildBridgeServerJar']) {
|
||||
rename 'corda-bridgeserver-(.*)', 'corda-bridgeserver.jar'
|
||||
into "net/corda/bridge/smoketest"
|
||||
}
|
||||
}
|
||||
|
||||
task smokeTest(type: Test) {
|
||||
testClassesDirs = sourceSets.smokeTest.output.classesDirs
|
||||
classpath = sourceSets.smokeTest.runtimeClasspath
|
||||
}
|
||||
|
||||
artifacts {
|
||||
runtimeArtifacts buildBridgeServerJar
|
||||
publish buildBridgeServerJar {
|
||||
|
@ -0,0 +1,241 @@
|
||||
package net.corda.bridge.smoketest
|
||||
|
||||
import com.nhaarman.mockito_kotlin.doReturn
|
||||
import com.nhaarman.mockito_kotlin.whenever
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.*
|
||||
import net.corda.core.node.NetworkParameters
|
||||
import net.corda.core.node.NotaryInfo
|
||||
import net.corda.core.node.services.AttachmentId
|
||||
import net.corda.core.serialization.SerializationDefaults
|
||||
import net.corda.core.serialization.serialize
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
import net.corda.core.utilities.contextLogger
|
||||
import net.corda.node.services.config.CertChainPolicyConfig
|
||||
import net.corda.node.services.config.EnterpriseConfiguration
|
||||
import net.corda.node.services.config.MutualExclusionConfiguration
|
||||
import net.corda.node.services.config.NodeConfiguration
|
||||
import net.corda.node.services.messaging.ArtemisMessagingServer
|
||||
import net.corda.nodeapi.internal.*
|
||||
import net.corda.nodeapi.internal.bridging.BridgeControl
|
||||
import net.corda.nodeapi.internal.config.NodeSSLConfiguration
|
||||
import net.corda.nodeapi.internal.config.SSLConfiguration
|
||||
import net.corda.nodeapi.internal.crypto.*
|
||||
import net.corda.nodeapi.internal.network.NetworkParametersCopier
|
||||
import net.corda.testing.core.*
|
||||
import net.corda.testing.internal.rigorousMock
|
||||
import org.apache.activemq.artemis.api.core.RoutingType
|
||||
import org.apache.activemq.artemis.api.core.SimpleString
|
||||
import org.apache.curator.test.TestingServer
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TemporaryFolder
|
||||
import java.net.Socket
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import java.security.cert.X509Certificate
|
||||
import java.time.Instant
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.streams.toList
|
||||
|
||||
class BridgeSmokeTest {
|
||||
companion object {
|
||||
val log = contextLogger()
|
||||
}
|
||||
|
||||
@Rule
|
||||
@JvmField
|
||||
val tempFolder = TemporaryFolder()
|
||||
|
||||
@Rule
|
||||
@JvmField
|
||||
val serializationEnvironment = SerializationEnvironmentRule(true)
|
||||
|
||||
private abstract class AbstractNodeConfiguration : NodeConfiguration
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
|
||||
}
|
||||
|
||||
@After
|
||||
fun cleanup() {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Run full features bridge from jar to ensure everything works`() {
|
||||
val artemisConfig = object : NodeSSLConfiguration {
|
||||
override val baseDirectory: Path = tempFolder.root.toPath()
|
||||
override val keyStorePassword: String = "cordacadevpass"
|
||||
override val trustStorePassword: String = "trustpass"
|
||||
override val crlCheckSoftFail: Boolean = true
|
||||
}
|
||||
artemisConfig.createBridgeKeyStores(DUMMY_BANK_A_NAME)
|
||||
copyBridgeResource("corda-bridgeserver.jar")
|
||||
copyBridgeResource("bridge.conf")
|
||||
createNetworkParams(tempFolder.root.toPath())
|
||||
val (artemisServer, artemisClient) = createArtemis()
|
||||
val zkServer = TestingServer(11105, false)
|
||||
try {
|
||||
installBridgeControlResponder(artemisClient)
|
||||
zkServer.start()
|
||||
val bridge = startBridge(tempFolder.root.toPath())
|
||||
waitForBridge(bridge)
|
||||
} finally {
|
||||
zkServer.close()
|
||||
artemisClient.stop()
|
||||
artemisServer.stop()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun copyBridgeResource(resourceName: String) {
|
||||
val testDir = tempFolder.root.toPath()
|
||||
// Find the finance jar file for the smoke tests of this module
|
||||
val bridgeJar = Paths.get("build", "resources/smokeTest/net/corda/bridge/smoketest").list {
|
||||
it.filter { resourceName in it.toString() }.toList().single()
|
||||
}
|
||||
bridgeJar.copyToDirectory(testDir)
|
||||
}
|
||||
|
||||
fun createNetworkParams(baseDirectory: Path) {
|
||||
val dummyNotaryParty = TestIdentity(DUMMY_NOTARY_NAME)
|
||||
val notaryInfo = NotaryInfo(dummyNotaryParty.party, false)
|
||||
val copier = NetworkParametersCopier(NetworkParameters(
|
||||
minimumPlatformVersion = 1,
|
||||
notaries = listOf(notaryInfo),
|
||||
modifiedTime = Instant.now(),
|
||||
maxMessageSize = 10485760,
|
||||
maxTransactionSize = 40000,
|
||||
epoch = 1,
|
||||
whitelistedContractImplementations = emptyMap<String, List<AttachmentId>>()
|
||||
), overwriteFile = true)
|
||||
copier.install(baseDirectory)
|
||||
}
|
||||
|
||||
fun SSLConfiguration.createBridgeKeyStores(legalName: CordaX500Name,
|
||||
rootCert: X509Certificate = DEV_ROOT_CA.certificate,
|
||||
intermediateCa: CertificateAndKeyPair = DEV_INTERMEDIATE_CA) {
|
||||
|
||||
certificatesDirectory.createDirectories()
|
||||
if (!trustStoreFile.exists()) {
|
||||
loadKeyStore(javaClass.classLoader.getResourceAsStream("certificates/${DEV_CA_TRUST_STORE_FILE}"), DEV_CA_TRUST_STORE_PASS).save(trustStoreFile, trustStorePassword)
|
||||
}
|
||||
|
||||
val (nodeCaCert, nodeCaKeyPair) = createDevNodeCa(intermediateCa, legalName)
|
||||
|
||||
val sslKeyStore = loadSslKeyStore(createNew = true)
|
||||
sslKeyStore.update {
|
||||
val tlsKeyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val tlsCert = X509Utilities.createCertificate(CertificateType.TLS, nodeCaCert, nodeCaKeyPair, legalName.x500Principal, tlsKeyPair.public)
|
||||
setPrivateKey(
|
||||
X509Utilities.CORDA_CLIENT_TLS,
|
||||
tlsKeyPair.private,
|
||||
listOf(tlsCert, nodeCaCert, intermediateCa.certificate, rootCert))
|
||||
}
|
||||
}
|
||||
|
||||
private fun startBridge(baseDirectory: Path): Process {
|
||||
val javaPath: Path = Paths.get(System.getProperty("java.home"), "bin", "java")
|
||||
val builder = ProcessBuilder()
|
||||
.command(javaPath.toString(), "-Dcapsule.log=verbose",
|
||||
"-jar", "corda-bridgeserver.jar")
|
||||
.directory(baseDirectory.toFile())
|
||||
.inheritIO()
|
||||
|
||||
builder.environment().putAll(mapOf(
|
||||
"CAPSULE_CACHE_DIR" to (baseDirectory / "capsule").toString()
|
||||
))
|
||||
|
||||
log.info("Start bridge process in $baseDirectory")
|
||||
return builder.start()
|
||||
}
|
||||
|
||||
private fun waitForBridge(process: Process) {
|
||||
var ok = false
|
||||
val executor = Executors.newSingleThreadScheduledExecutor()
|
||||
try {
|
||||
executor.scheduleWithFixedDelay({
|
||||
try {
|
||||
if (!process.isAlive) {
|
||||
log.error("Bridge has died.")
|
||||
return@scheduleWithFixedDelay
|
||||
}
|
||||
if (!serverListening("localhost", 10005)) {
|
||||
log.warn("Bridge not listening yet")
|
||||
return@scheduleWithFixedDelay
|
||||
}
|
||||
|
||||
ok = true
|
||||
|
||||
// Cancel the polling
|
||||
executor.shutdown()
|
||||
} catch (e: Exception) {
|
||||
log.warn("Bridge not ready yet (Error: {})", e.message)
|
||||
}
|
||||
}, 5, 1, TimeUnit.SECONDS)
|
||||
|
||||
val setupOK = executor.awaitTermination(60, TimeUnit.SECONDS)
|
||||
check(setupOK && ok && process.isAlive) { "Bridge Failed to open listening port" }
|
||||
} catch (e: Exception) {
|
||||
throw e
|
||||
} finally {
|
||||
executor.shutdownNow()
|
||||
process.destroyForcibly()
|
||||
}
|
||||
}
|
||||
|
||||
fun serverListening(host: String, port: Int): Boolean {
|
||||
var s: Socket? = null
|
||||
try {
|
||||
s = Socket(host, port)
|
||||
return true
|
||||
} catch (e: Exception) {
|
||||
return false
|
||||
} finally {
|
||||
try {
|
||||
s?.close()
|
||||
} catch (e: Exception) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createArtemis(): Pair<ArtemisMessagingServer, ArtemisMessagingClient> {
|
||||
val artemisConfig = rigorousMock<AbstractNodeConfiguration>().also {
|
||||
doReturn(tempFolder.root.toPath()).whenever(it).baseDirectory
|
||||
doReturn(ALICE_NAME).whenever(it).myLegalName
|
||||
doReturn("trustpass").whenever(it).trustStorePassword
|
||||
doReturn("cordacadevpass").whenever(it).keyStorePassword
|
||||
doReturn(NetworkHostAndPort("localhost", 11005)).whenever(it).p2pAddress
|
||||
doReturn(null).whenever(it).jmxMonitoringHttpPort
|
||||
doReturn(emptyList<CertChainPolicyConfig>()).whenever(it).certificateChainCheckPolicies
|
||||
doReturn(EnterpriseConfiguration(MutualExclusionConfiguration(false, "", 20000, 40000), externalBridge = true)).whenever(it).enterpriseConfiguration
|
||||
}
|
||||
val artemisServer = ArtemisMessagingServer(artemisConfig, NetworkHostAndPort("0.0.0.0", 11005), MAX_MESSAGE_SIZE)
|
||||
val artemisClient = ArtemisMessagingClient(artemisConfig, NetworkHostAndPort("localhost", 11005), MAX_MESSAGE_SIZE)
|
||||
artemisServer.start()
|
||||
artemisClient.start()
|
||||
return Pair(artemisServer, artemisClient)
|
||||
}
|
||||
|
||||
private fun installBridgeControlResponder(artemisClient: ArtemisMessagingClient) {
|
||||
val artemis = artemisClient.started!!
|
||||
val inboxAddress = SimpleString("${ArtemisMessagingComponent.P2P_PREFIX}Test")
|
||||
artemis.session.createQueue(inboxAddress, RoutingType.ANYCAST, inboxAddress, true)
|
||||
artemis.session.createQueue(ArtemisMessagingComponent.BRIDGE_NOTIFY, RoutingType.ANYCAST, ArtemisMessagingComponent.BRIDGE_NOTIFY, false)
|
||||
val controlConsumer = artemis.session.createConsumer(ArtemisMessagingComponent.BRIDGE_NOTIFY)
|
||||
controlConsumer.setMessageHandler { msg ->
|
||||
val bridgeControl = BridgeControl.NodeToBridgeSnapshot("Test", listOf(inboxAddress.toString()), emptyList())
|
||||
val controlPacket = bridgeControl.serialize(context = SerializationDefaults.P2P_CONTEXT).bytes
|
||||
val artemisMessage = artemis.session.createMessage(false)
|
||||
artemisMessage.writeBodyBufferBytes(controlPacket)
|
||||
artemis.producer.send(ArtemisMessagingComponent.BRIDGE_CONTROL, artemisMessage)
|
||||
msg.acknowledge()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
//
|
||||
// R3 Proprietary and Confidential
|
||||
//
|
||||
// Copyright (c) 2018 R3 Limited. All rights reserved.
|
||||
//
|
||||
// The intellectual and technical concepts contained herein are proprietary to R3 and its suppliers and are protected by trade secret law.
|
||||
//
|
||||
// Distribution of this file or any portion thereof via any medium without the express permission of R3 is strictly prohibited.
|
||||
|
||||
bridgeMode = SenderReceiver
|
||||
outboundConfig : {
|
||||
artemisBrokerAddress = "localhost:11005"
|
||||
socksProxyConfig : {
|
||||
version = SOCKS5
|
||||
proxyAddress = "localhost:12345"
|
||||
userName = "proxyUser"
|
||||
password = "pwd"
|
||||
}
|
||||
}
|
||||
inboundConfig : {
|
||||
listeningAddress = "0.0.0.0:10005"
|
||||
}
|
||||
haConfig : {
|
||||
haConnectionString = "zk://localhost:11105"
|
||||
}
|
||||
networkParametersPath = network-parameters
|
@ -34,7 +34,41 @@ processResources {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile project(':node-api')
|
||||
|
||||
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||
compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
|
||||
testCompile "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
||||
|
||||
compile(project(':core')) {
|
||||
transitive = false // we control dependencies directly as the bridge is likely to be audited
|
||||
}
|
||||
|
||||
compile(project(':node-api')) {
|
||||
transitive = false// we control dependencies directly as the bridge is likely to be audited
|
||||
}
|
||||
|
||||
// Here we pull in dependencies that would normally be pulled in transitively from :core and :node-api, but we need more fine grained control
|
||||
// For AMQP serialisation.
|
||||
compile "org.apache.qpid:proton-j:${protonj_version}"
|
||||
// RxJava: observable streams of events.
|
||||
compile "io.reactivex:rxjava:$rxjava_version"
|
||||
compile("org.apache.activemq:artemis-core-client:${artemis_version}")
|
||||
compile "org.apache.activemq:artemis-commons:${artemis_version}"
|
||||
// Netty: All of it! Dn't depend upon ActiveMQ to pull it in correctly
|
||||
compile "io.netty:netty-all:$netty_version"
|
||||
// TypeSafe Config: for simple and human friendly config files.
|
||||
compile "com.typesafe:config:$typesafe_config_version"
|
||||
|
||||
// The following dependencies are required to load and deserialize network-info, or other static initializers that get triggered.
|
||||
// Java ed25519 implementation. See https://github.com/str4d/ed25519-java/ for deserializing our public keys and certs.
|
||||
compile "net.i2p.crypto:eddsa:$eddsa_version"
|
||||
// Bouncy castle support needed for X509 certificate manipulation
|
||||
compile "org.bouncycastle:bcprov-jdk15on:${bouncycastle_version}"
|
||||
compile "org.bouncycastle:bcpkix-jdk15on:${bouncycastle_version}"
|
||||
// Seems to be needed?
|
||||
compile "com.github.ben-manes.caffeine:caffeine:$caffeine_version"
|
||||
// Pulled in by whitelist
|
||||
compile "com.esotericsoftware:kryo:4.0.0"
|
||||
|
||||
// Log4J: logging framework (with SLF4J bindings)
|
||||
compile "org.apache.logging.log4j:log4j-slf4j-impl:$log4j_version"
|
||||
@ -50,6 +84,7 @@ dependencies {
|
||||
integrationTestCompile project(':node-driver')
|
||||
integrationTestCompile "org.apache.curator:curator-test:${curator_version}"
|
||||
testCompile "junit:junit:$junit_version"
|
||||
testCompile "org.apache.curator:curator-test:${curator_version}"
|
||||
testCompile project(':test-utils')
|
||||
}
|
||||
|
||||
|
@ -93,6 +93,7 @@ buildscript {
|
||||
ext.eaagentloader_version = '1.0.3'
|
||||
ext.curator_version = '4.0.0'
|
||||
ext.jsch_version = '0.1.54'
|
||||
ext.protonj_version = '0.27.1'
|
||||
|
||||
// Update 121 is required for ObjectInputFilter and at time of writing 131 was latest:
|
||||
ext.java8_minUpdateVersion = '131'
|
||||
|
@ -44,7 +44,7 @@ dependencies {
|
||||
compile "de.javakaffee:kryo-serializers:0.41"
|
||||
|
||||
// For AMQP serialisation.
|
||||
compile "org.apache.qpid:proton-j:0.27.1"
|
||||
compile "org.apache.qpid:proton-j:${protonj_version}"
|
||||
|
||||
// SQL connection pooling library
|
||||
compile "com.zaxxer:HikariCP:$hikari_version"
|
||||
|
Loading…
x
Reference in New Issue
Block a user