Support HA without load balancer (#3889)

Allow configuration in node for additional advertised addresses.

fix logic error

Use empty list as default config not null

Allow multiple addresses in NodeInfo

Describe new additionalP2PAddresses property in docs.

Add integration test of additionalP2PAddress feature

Fixup after rebase

Address PR comment

Address PR comments by removing unused element of NodeAddress
This commit is contained in:
Matthew Nesbit
2018-09-05 17:46:46 +01:00
committed by GitHub
parent e3ece00bea
commit 304dba704e
12 changed files with 133 additions and 75 deletions

View File

@ -96,6 +96,9 @@ absolute path to the node's base directory.
note that the host is the included as the advertised entry in the network map. As a result the value listed note that the host is the included as the advertised entry in the network map. As a result the value listed
here must be externally accessible when running nodes across a cluster of machines. If the provided host is unreachable, here must be externally accessible when running nodes across a cluster of machines. If the provided host is unreachable,
the node will try to auto-discover its public one. the node will try to auto-discover its public one.
:additionalP2PAddresses: An array of additional host:port values, which will be included in the advertised NodeInfo in the network map in addition to the ``p2pAddress``.
Nodes can use this configuration option to advertise HA endpoints and aliases to external parties. If not specified the default value is an empty list.
:flowTimeout: When a flow implementing the ``TimedFlow`` interface does not complete in time, it is restarted from the :flowTimeout: When a flow implementing the ``TimedFlow`` interface does not complete in time, it is restarted from the
initial checkpoint. Currently only used for notarisation requests: if a notary replica dies while processing a notarisation request, initial checkpoint. Currently only used for notarisation requests: if a notary replica dies while processing a notarisation request,

View File

@ -6,7 +6,6 @@ import net.corda.core.messaging.MessageRecipientGroup
import net.corda.core.messaging.MessageRecipients import net.corda.core.messaging.MessageRecipients
import net.corda.core.messaging.SingleMessageRecipient import net.corda.core.messaging.SingleMessageRecipient
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
import net.corda.core.utilities.NetworkHostAndPort
import org.apache.activemq.artemis.api.core.Message import org.apache.activemq.artemis.api.core.Message
import org.apache.activemq.artemis.api.core.SimpleString import org.apache.activemq.artemis.api.core.SimpleString
import java.security.PublicKey import java.security.PublicKey
@ -77,9 +76,7 @@ class ArtemisMessagingComponent {
val queueName: String val queueName: String
} }
interface ArtemisPeerAddress : ArtemisAddress, SingleMessageRecipient { interface ArtemisPeerAddress : ArtemisAddress, SingleMessageRecipient
val hostAndPort: NetworkHostAndPort
}
/** /**
* This is the class used to implement [SingleMessageRecipient], for now. Note that in future this class * This is the class used to implement [SingleMessageRecipient], for now. Note that in future this class
@ -90,12 +87,11 @@ class ArtemisMessagingComponent {
* an advertised service's queue. * an advertised service's queue.
* *
* @param queueName The name of the queue this address is associated with. * @param queueName The name of the queue this address is associated with.
* @param hostAndPort The address of the node.
*/ */
@CordaSerializable @CordaSerializable
data class NodeAddress(override val queueName: String, override val hostAndPort: NetworkHostAndPort) : ArtemisPeerAddress { data class NodeAddress(override val queueName: String) : ArtemisPeerAddress {
constructor(peerIdentity: PublicKey, hostAndPort: NetworkHostAndPort) : constructor(peerIdentity: PublicKey) :
this("$PEERS_PREFIX${peerIdentity.toStringShort()}", hostAndPort) this("$PEERS_PREFIX${peerIdentity.toStringShort()}")
} }
/** /**

View File

@ -4,16 +4,13 @@ import io.netty.channel.EventLoopGroup
import io.netty.channel.nio.NioEventLoopGroup import io.netty.channel.nio.NioEventLoopGroup
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.VisibleForTesting import net.corda.core.internal.VisibleForTesting
import net.corda.core.node.NodeInfo
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
import net.corda.core.utilities.contextLogger import net.corda.core.utilities.contextLogger
import net.corda.nodeapi.internal.ArtemisMessagingClient import net.corda.nodeapi.internal.ArtemisMessagingClient
import net.corda.nodeapi.internal.ArtemisMessagingComponent
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NODE_P2P_USER import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.NODE_P2P_USER
import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.P2PMessagingHeaders import net.corda.nodeapi.internal.ArtemisMessagingComponent.Companion.P2PMessagingHeaders
import net.corda.nodeapi.internal.ArtemisMessagingComponent.RemoteInboxAddress.Companion.translateLocalQueueToInboxAddress import net.corda.nodeapi.internal.ArtemisMessagingComponent.RemoteInboxAddress.Companion.translateLocalQueueToInboxAddress
import net.corda.nodeapi.internal.ArtemisSessionProvider import net.corda.nodeapi.internal.ArtemisSessionProvider
import net.corda.nodeapi.internal.bridging.AMQPBridgeManager.AMQPBridge.Companion.getBridgeName
import net.corda.nodeapi.internal.config.CertificateStore import net.corda.nodeapi.internal.config.CertificateStore
import net.corda.nodeapi.internal.config.MutualSslConfiguration import net.corda.nodeapi.internal.config.MutualSslConfiguration
import net.corda.nodeapi.internal.protonwrapper.messages.MessageStatus import net.corda.nodeapi.internal.protonwrapper.messages.MessageStatus
@ -40,7 +37,7 @@ import kotlin.concurrent.withLock
class AMQPBridgeManager(config: MutualSslConfiguration, maxMessageSize: Int, private val artemisMessageClientFactory: () -> ArtemisSessionProvider) : BridgeManager { class AMQPBridgeManager(config: MutualSslConfiguration, maxMessageSize: Int, private val artemisMessageClientFactory: () -> ArtemisSessionProvider) : BridgeManager {
private val lock = ReentrantLock() private val lock = ReentrantLock()
private val bridgeNameToBridgeMap = mutableMapOf<String, AMQPBridge>() private val queueNamesToBridgesMap = mutableMapOf<String, MutableList<AMQPBridge>>()
private class AMQPConfigurationImpl private constructor(override val keyStore: CertificateStore, private class AMQPConfigurationImpl private constructor(override val keyStore: CertificateStore,
override val trustStore: CertificateStore, override val trustStore: CertificateStore,
@ -66,14 +63,13 @@ class AMQPBridgeManager(config: MutualSslConfiguration, maxMessageSize: Int, pri
* If the delivery fails the session is rolled back to prevent loss of the message. This may cause duplicate delivery, * If the delivery fails the session is rolled back to prevent loss of the message. This may cause duplicate delivery,
* however Artemis and the remote Corda instanced will deduplicate these messages. * however Artemis and the remote Corda instanced will deduplicate these messages.
*/ */
private class AMQPBridge(private val queueName: String, private class AMQPBridge(val queueName: String,
private val target: NetworkHostAndPort, val targets: List<NetworkHostAndPort>,
private val legalNames: Set<CordaX500Name>, private val legalNames: Set<CordaX500Name>,
private val amqpConfig: AMQPConfiguration, private val amqpConfig: AMQPConfiguration,
sharedEventGroup: EventLoopGroup, sharedEventGroup: EventLoopGroup,
private val artemis: ArtemisSessionProvider) { private val artemis: ArtemisSessionProvider) {
companion object { companion object {
fun getBridgeName(queueName: String, hostAndPort: NetworkHostAndPort): String = "$queueName -> $hostAndPort"
private val log = contextLogger() private val log = contextLogger()
} }
@ -81,8 +77,7 @@ class AMQPBridgeManager(config: MutualSslConfiguration, maxMessageSize: Int, pri
val oldMDC = MDC.getCopyOfContextMap() val oldMDC = MDC.getCopyOfContextMap()
try { try {
MDC.put("queueName", queueName) MDC.put("queueName", queueName)
MDC.put("target", target.toString()) MDC.put("targets", targets.joinToString(separator = ";") { it.toString() })
MDC.put("bridgeName", bridgeName)
MDC.put("legalNames", legalNames.joinToString(separator = ";") { it.toString() }) MDC.put("legalNames", legalNames.joinToString(separator = ";") { it.toString() })
MDC.put("maxMessageSize", amqpConfig.maxMessageSize.toString()) MDC.put("maxMessageSize", amqpConfig.maxMessageSize.toString())
block() block()
@ -101,8 +96,7 @@ class AMQPBridgeManager(config: MutualSslConfiguration, maxMessageSize: Int, pri
private fun logWarnWithMDC(msg: String) = withMDC { log.warn(msg) } private fun logWarnWithMDC(msg: String) = withMDC { log.warn(msg) }
val amqpClient = AMQPClient(listOf(target), legalNames, amqpConfig, sharedThreadPool = sharedEventGroup) val amqpClient = AMQPClient(targets, legalNames, amqpConfig, sharedThreadPool = sharedEventGroup)
val bridgeName: String get() = getBridgeName(queueName, target)
private val lock = ReentrantLock() // lock to serialise session level access private val lock = ReentrantLock() // lock to serialise session level access
private var session: ClientSession? = null private var session: ClientSession? = null
private var consumer: ClientConsumer? = null private var consumer: ClientConsumer? = null
@ -194,39 +188,37 @@ class AMQPBridgeManager(config: MutualSslConfiguration, maxMessageSize: Int, pri
} }
} }
private fun gatherAddresses(node: NodeInfo): List<ArtemisMessagingComponent.NodeAddress> { override fun deployBridge(queueName: String, targets: List<NetworkHostAndPort>, legalNames: Set<CordaX500Name>) {
return node.legalIdentitiesAndCerts.map { ArtemisMessagingComponent.NodeAddress(it.party.owningKey, node.addresses[0]) } val newBridge = lock.withLock {
} val bridges = queueNamesToBridgesMap.getOrPut(queueName) { mutableListOf() }
for (target in targets) {
override fun deployBridge(queueName: String, target: NetworkHostAndPort, legalNames: Set<CordaX500Name>) { if (bridges.any { it.targets.contains(target) }) {
if (bridgeExists(getBridgeName(queueName, target))) { return
return }
} }
val newBridge = AMQPBridge(queueName, target, legalNames, amqpConfig, sharedEventLoopGroup!!, artemis!!) val newBridge = AMQPBridge(queueName, targets, legalNames, amqpConfig, sharedEventLoopGroup!!, artemis!!)
lock.withLock { bridges += newBridge
bridgeNameToBridgeMap[newBridge.bridgeName] = newBridge newBridge
} }
newBridge.start() newBridge.start()
} }
override fun destroyBridges(node: NodeInfo) { override fun destroyBridge(queueName: String, targets: List<NetworkHostAndPort>) {
lock.withLock { lock.withLock {
gatherAddresses(node).forEach { val bridges = queueNamesToBridgesMap[queueName] ?: mutableListOf()
val bridge = bridgeNameToBridgeMap.remove(getBridgeName(it.queueName, it.hostAndPort)) for (target in targets) {
bridge?.stop() val bridge = bridges.firstOrNull { it.targets.contains(target) }
if (bridge != null) {
bridges -= bridge
if (bridges.isEmpty()) {
queueNamesToBridgesMap.remove(queueName)
}
bridge.stop()
}
} }
} }
} }
override fun destroyBridge(queueName: String, hostAndPort: NetworkHostAndPort) {
lock.withLock {
val bridge = bridgeNameToBridgeMap.remove(getBridgeName(queueName, hostAndPort))
bridge?.stop()
}
}
override fun bridgeExists(bridgeName: String): Boolean = lock.withLock { bridgeNameToBridgeMap.containsKey(bridgeName) }
override fun start() { override fun start() {
sharedEventLoopGroup = NioEventLoopGroup(NUM_BRIDGE_THREADS) sharedEventLoopGroup = NioEventLoopGroup(NUM_BRIDGE_THREADS)
val artemis = artemisMessageClientFactory() val artemis = artemisMessageClientFactory()
@ -238,13 +230,13 @@ class AMQPBridgeManager(config: MutualSslConfiguration, maxMessageSize: Int, pri
override fun close() { override fun close() {
lock.withLock { lock.withLock {
for (bridge in bridgeNameToBridgeMap.values) { for (bridge in queueNamesToBridgesMap.values.flatten()) {
bridge.stop() bridge.stop()
} }
sharedEventLoopGroup?.shutdownGracefully() sharedEventLoopGroup?.shutdownGracefully()
sharedEventLoopGroup?.terminationFuture()?.sync() sharedEventLoopGroup?.terminationFuture()?.sync()
sharedEventLoopGroup = null sharedEventLoopGroup = null
bridgeNameToBridgeMap.clear() queueNamesToBridgesMap.clear()
artemis?.stop() artemis?.stop()
} }
} }

View File

@ -98,7 +98,7 @@ class BridgeControlListener(val config: MutualSslConfiguration,
return return
} }
for (outQueue in controlMessage.sendQueues) { for (outQueue in controlMessage.sendQueues) {
bridgeManager.deployBridge(outQueue.queueName, outQueue.targets.first(), outQueue.legalNames.toSet()) bridgeManager.deployBridge(outQueue.queueName, outQueue.targets, outQueue.legalNames.toSet())
} }
validInboundQueues.addAll(controlMessage.inboxQueues) validInboundQueues.addAll(controlMessage.inboxQueues)
} }
@ -110,14 +110,14 @@ class BridgeControlListener(val config: MutualSslConfiguration,
log.error("Invalid queue names in control message $controlMessage") log.error("Invalid queue names in control message $controlMessage")
return return
} }
bridgeManager.deployBridge(controlMessage.bridgeInfo.queueName, controlMessage.bridgeInfo.targets.first(), controlMessage.bridgeInfo.legalNames.toSet()) bridgeManager.deployBridge(controlMessage.bridgeInfo.queueName, controlMessage.bridgeInfo.targets, controlMessage.bridgeInfo.legalNames.toSet())
} }
is BridgeControl.Delete -> { is BridgeControl.Delete -> {
if (!controlMessage.bridgeInfo.queueName.startsWith(PEERS_PREFIX)) { if (!controlMessage.bridgeInfo.queueName.startsWith(PEERS_PREFIX)) {
log.error("Invalid queue names in control message $controlMessage") log.error("Invalid queue names in control message $controlMessage")
return return
} }
bridgeManager.destroyBridge(controlMessage.bridgeInfo.queueName, controlMessage.bridgeInfo.targets.first()) bridgeManager.destroyBridge(controlMessage.bridgeInfo.queueName, controlMessage.bridgeInfo.targets)
} }
} }
} }

View File

@ -2,7 +2,6 @@ package net.corda.nodeapi.internal.bridging
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.VisibleForTesting import net.corda.core.internal.VisibleForTesting
import net.corda.core.node.NodeInfo
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
/** /**
@ -10,13 +9,9 @@ import net.corda.core.utilities.NetworkHostAndPort
*/ */
@VisibleForTesting @VisibleForTesting
interface BridgeManager : AutoCloseable { interface BridgeManager : AutoCloseable {
fun deployBridge(queueName: String, target: NetworkHostAndPort, legalNames: Set<CordaX500Name>) fun deployBridge(queueName: String, targets: List<NetworkHostAndPort>, legalNames: Set<CordaX500Name>)
fun destroyBridges(node: NodeInfo) fun destroyBridge(queueName: String, targets: List<NetworkHostAndPort>)
fun destroyBridge(queueName: String, hostAndPort: NetworkHostAndPort)
fun bridgeExists(bridgeName: String): Boolean
fun start() fun start()

View File

@ -193,7 +193,7 @@ class AMQPBridgeTest {
if (sourceQueueName != null) { if (sourceQueueName != null) {
// Local queue for outgoing messages // Local queue for outgoing messages
artemis.session.createQueue(sourceQueueName, RoutingType.ANYCAST, sourceQueueName, true) artemis.session.createQueue(sourceQueueName, RoutingType.ANYCAST, sourceQueueName, true)
bridgeManager.deployBridge(sourceQueueName, amqpAddress, setOf(BOB.name)) bridgeManager.deployBridge(sourceQueueName, listOf(amqpAddress), setOf(BOB.name))
} }
return Triple(artemisServer, artemisClient, bridgeManager) return Triple(artemisServer, artemisClient, bridgeManager)
} }

View File

@ -0,0 +1,70 @@
package net.corda.services.messaging
import com.typesafe.config.ConfigValueFactory
import junit.framework.TestCase.assertEquals
import net.corda.client.rpc.CordaRPCClient
import net.corda.core.contracts.Amount
import net.corda.core.contracts.Issued
import net.corda.core.contracts.withoutIssuer
import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.getOrThrow
import net.corda.finance.DOLLARS
import net.corda.finance.contracts.asset.Cash
import net.corda.finance.flows.CashIssueAndPaymentFlow
import net.corda.node.services.Permissions.Companion.all
import net.corda.testing.core.DUMMY_BANK_A_NAME
import net.corda.testing.core.DUMMY_BANK_B_NAME
import net.corda.testing.core.expect
import net.corda.testing.core.expectEvents
import net.corda.testing.driver.DriverParameters
import net.corda.testing.driver.PortAllocation
import net.corda.testing.driver.driver
import net.corda.testing.node.User
import org.junit.Test
import java.util.*
class AdditionP2PAddressModeTest {
private val portAllocation = PortAllocation.Incremental(27182)
@Test
fun `runs nodes with one configured to use additionalP2PAddresses`() {
val testUser = User("test", "test", setOf(all()))
driver(DriverParameters(startNodesInProcess = true, inMemoryDB = true, extraCordappPackagesToScan = listOf("net.corda.finance"))) {
val mainAddress = portAllocation.nextHostAndPort().toString()
val altAddress = portAllocation.nextHostAndPort().toString()
val haConfig = mutableMapOf<String, Any?>()
haConfig["detectPublicIp"] = false
haConfig["p2pAddress"] = mainAddress //advertise this as primary
haConfig["messagingServerAddress"] = altAddress // but actually host on the alternate address
haConfig["messagingServerExternal"] = false
haConfig["additionalP2PAddresses"] = ConfigValueFactory.fromIterable(listOf(altAddress)) // advertise this secondary address
val (nodeA, nodeB) = listOf(
startNode(providedName = DUMMY_BANK_A_NAME, rpcUsers = listOf(testUser), customOverrides = haConfig),
startNode(providedName = DUMMY_BANK_B_NAME, rpcUsers = listOf(testUser), customOverrides = mapOf("p2pAddress" to portAllocation.nextHostAndPort().toString()))
).map { it.getOrThrow() }
val (nodeARpc, nodeBRpc) = listOf(nodeA, nodeB).map {
val client = CordaRPCClient(it.rpcAddress)
client.start(testUser.username, testUser.password).proxy
}
val nodeBVaultUpdates = nodeBRpc.vaultTrack(Cash.State::class.java).updates
val issueRef = OpaqueBytes.of(0.toByte())
nodeARpc.startFlowDynamic(
CashIssueAndPaymentFlow::class.java,
DOLLARS(1234),
issueRef,
nodeB.nodeInfo.legalIdentities.get(0),
true,
defaultNotaryIdentity
).returnValue.getOrThrow()
nodeBVaultUpdates.expectEvents {
expect { update ->
println("Bob got vault update of $update")
val amount: Amount<Issued<Currency>> = update.produced.first().state.data.amount
assertEquals(1234.DOLLARS, amount.withoutIssuer())
}
}
}
}
}

View File

@ -42,11 +42,7 @@ import net.corda.node.services.Permissions
import net.corda.node.services.api.FlowStarter import net.corda.node.services.api.FlowStarter
import net.corda.node.services.api.ServiceHubInternal import net.corda.node.services.api.ServiceHubInternal
import net.corda.node.services.api.StartedNodeServices import net.corda.node.services.api.StartedNodeServices
import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.*
import net.corda.node.services.config.SecurityConfiguration
import net.corda.node.services.config.shouldInitCrashShell
import net.corda.node.services.config.shouldStartLocalShell
import net.corda.node.services.config.JmxReporterType
import net.corda.node.services.messaging.* import net.corda.node.services.messaging.*
import net.corda.node.services.rpc.ArtemisRpcBroker import net.corda.node.services.rpc.ArtemisRpcBroker
import net.corda.node.utilities.AddressUtils import net.corda.node.utilities.AddressUtils
@ -58,8 +54,8 @@ import net.corda.nodeapi.internal.addShutdownHook
import net.corda.nodeapi.internal.bridging.BridgeControlListener import net.corda.nodeapi.internal.bridging.BridgeControlListener
import net.corda.nodeapi.internal.config.User import net.corda.nodeapi.internal.config.User
import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.crypto.X509Utilities
import net.corda.serialization.internal.*
import net.corda.nodeapi.internal.persistence.CouldNotCreateDataSourceException import net.corda.nodeapi.internal.persistence.CouldNotCreateDataSourceException
import net.corda.serialization.internal.*
import org.h2.jdbc.JdbcSQLException import org.h2.jdbc.JdbcSQLException
import org.slf4j.Logger import org.slf4j.Logger
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
@ -71,10 +67,10 @@ import java.net.InetAddress
import java.nio.file.Path import java.nio.file.Path
import java.nio.file.Paths import java.nio.file.Paths
import java.time.Clock import java.time.Clock
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicInteger
import javax.management.ObjectName import javax.management.ObjectName
import kotlin.system.exitProcess import kotlin.system.exitProcess
import java.util.concurrent.TimeUnit
class NodeWithInfo(val node: Node, val info: NodeInfo) { class NodeWithInfo(val node: Node, val info: NodeInfo) {
val services: StartedNodeServices = object : StartedNodeServices, ServiceHubInternal by node.services, FlowStarter by node.flowStarter {} val services: StartedNodeServices = object : StartedNodeServices, ServiceHubInternal by node.services, FlowStarter by node.flowStarter {}
@ -282,7 +278,7 @@ open class Node(configuration: NodeConfiguration,
} }
} }
override fun myAddresses(): List<NetworkHostAndPort> = listOf(getAdvertisedAddress()) override fun myAddresses(): List<NetworkHostAndPort> = listOf(getAdvertisedAddress()) + configuration.additionalP2PAddresses
private fun getAdvertisedAddress(): NetworkHostAndPort { private fun getAdvertisedAddress(): NetworkHostAndPort {
return with(configuration) { return with(configuration) {

View File

@ -50,6 +50,7 @@ interface NodeConfiguration {
val notary: NotaryConfig? val notary: NotaryConfig?
val additionalNodeInfoPollingFrequencyMsec: Long val additionalNodeInfoPollingFrequencyMsec: Long
val p2pAddress: NetworkHostAndPort val p2pAddress: NetworkHostAndPort
val additionalP2PAddresses: List<NetworkHostAndPort>
val rpcOptions: NodeRpcOptions val rpcOptions: NodeRpcOptions
val messagingServerAddress: NetworkHostAndPort? val messagingServerAddress: NetworkHostAndPort?
val messagingServerExternal: Boolean val messagingServerExternal: Boolean
@ -198,6 +199,7 @@ data class NodeConfigurationImpl(
override val verifierType: VerifierType, override val verifierType: VerifierType,
override val flowTimeout: FlowTimeoutConfiguration, override val flowTimeout: FlowTimeoutConfiguration,
override val p2pAddress: NetworkHostAndPort, override val p2pAddress: NetworkHostAndPort,
override val additionalP2PAddresses: List<NetworkHostAndPort> = emptyList(),
private val rpcAddress: NetworkHostAndPort? = null, private val rpcAddress: NetworkHostAndPort? = null,
private val rpcSettings: NodeRpcSettings, private val rpcSettings: NodeRpcSettings,
override val messagingServerAddress: NetworkHostAndPort?, override val messagingServerAddress: NetworkHostAndPort?,

View File

@ -120,7 +120,7 @@ class P2PMessagingClient(val config: NodeConfiguration,
private lateinit var advertisedAddress: NetworkHostAndPort private lateinit var advertisedAddress: NetworkHostAndPort
private var maxMessageSize: Int = -1 private var maxMessageSize: Int = -1
override val myAddress: SingleMessageRecipient get() = NodeAddress(myIdentity, advertisedAddress) override val myAddress: SingleMessageRecipient get() = NodeAddress(myIdentity)
override val ourSenderUUID = UUID.randomUUID().toString() override val ourSenderUUID = UUID.randomUUID().toString()
private val state = ThreadBox(InnerState()) private val state = ThreadBox(InnerState())
@ -233,7 +233,7 @@ class P2PMessagingClient(val config: NodeConfiguration,
fun gatherAddresses(node: NodeInfo): Sequence<BridgeEntry> { fun gatherAddresses(node: NodeInfo): Sequence<BridgeEntry> {
return state.locked { return state.locked {
node.legalIdentitiesAndCerts.map { node.legalIdentitiesAndCerts.map {
val messagingAddress = NodeAddress(it.party.owningKey, node.addresses.first()) val messagingAddress = NodeAddress(it.party.owningKey)
BridgeEntry(messagingAddress.queueName, node.addresses, node.legalIdentities.map { it.name }) BridgeEntry(messagingAddress.queueName, node.addresses, node.legalIdentities.map { it.name })
}.filter { producerSession!!.queueQuery(SimpleString(it.queueName)).isExists }.asSequence() }.filter { producerSession!!.queueQuery(SimpleString(it.queueName)).isExists }.asSequence()
} }
@ -242,14 +242,14 @@ class P2PMessagingClient(val config: NodeConfiguration,
fun deployBridges(node: NodeInfo) { fun deployBridges(node: NodeInfo) {
gatherAddresses(node) gatherAddresses(node)
.forEach { .forEach {
sendBridgeControl(BridgeControl.Create(myIdentity.toStringShort(), it)) sendBridgeControl(BridgeControl.Create(config.myLegalName.toString(), it))
} }
} }
fun destroyBridges(node: NodeInfo) { fun destroyBridges(node: NodeInfo) {
gatherAddresses(node) gatherAddresses(node)
.forEach { .forEach {
sendBridgeControl(BridgeControl.Delete(myIdentity.toStringShort(), it)) sendBridgeControl(BridgeControl.Delete(config.myLegalName.toString(), it))
} }
} }
@ -289,7 +289,7 @@ class P2PMessagingClient(val config: NodeConfiguration,
delayStartQueues += queue.toString() delayStartQueues += queue.toString()
} }
} }
val startupMessage = BridgeControl.NodeToBridgeSnapshot(myIdentity.toStringShort(), inboxes, requiredBridges) val startupMessage = BridgeControl.NodeToBridgeSnapshot(config.myLegalName.toString(), inboxes, requiredBridges)
sendBridgeControl(startupMessage) sendBridgeControl(startupMessage)
} }
@ -495,7 +495,7 @@ class P2PMessagingClient(val config: NodeConfiguration,
val peers = networkMap.getNodesByOwningKeyIndex(keyHash) val peers = networkMap.getNodesByOwningKeyIndex(keyHash)
for (node in peers) { for (node in peers) {
val bridge = BridgeEntry(queueName, node.addresses, node.legalIdentities.map { it.name }) val bridge = BridgeEntry(queueName, node.addresses, node.legalIdentities.map { it.name })
val createBridgeMessage = BridgeControl.Create(myIdentity.toStringShort(), bridge) val createBridgeMessage = BridgeControl.Create(config.myLegalName.toString(), bridge)
sendBridgeControl(createBridgeMessage) sendBridgeControl(createBridgeMessage)
} }
} }
@ -540,7 +540,7 @@ class P2PMessagingClient(val config: NodeConfiguration,
override fun getAddressOfParty(partyInfo: PartyInfo): MessageRecipients { override fun getAddressOfParty(partyInfo: PartyInfo): MessageRecipients {
return when (partyInfo) { return when (partyInfo) {
is PartyInfo.SingleNode -> NodeAddress(partyInfo.party.owningKey, partyInfo.addresses.single()) is PartyInfo.SingleNode -> NodeAddress(partyInfo.party.owningKey)
is PartyInfo.DistributedNode -> ServiceAddress(partyInfo.party.owningKey) is PartyInfo.DistributedNode -> ServiceAddress(partyInfo.party.owningKey)
} }
} }

View File

@ -3,6 +3,7 @@ keyStorePassword = "cordacadevpass"
trustStorePassword = "trustpass" trustStorePassword = "trustpass"
crlCheckSoftFail = true crlCheckSoftFail = true
lazyBridgeStart = true lazyBridgeStart = true
additionalP2PAddresses = []
dataSourceProperties = { dataSourceProperties = {
dataSourceClassName = org.h2.jdbcx.JdbcDataSource dataSourceClassName = org.h2.jdbcx.JdbcDataSource
dataSource.url = "jdbc:h2:file:"${baseDirectory}"/persistence;DB_CLOSE_ON_EXIT=FALSE;WRITE_DELAY=0;LOCK_TIMEOUT=10000" dataSource.url = "jdbc:h2:file:"${baseDirectory}"/persistence;DB_CLOSE_ON_EXIT=FALSE;WRITE_DELAY=0;LOCK_TIMEOUT=10000"

View File

@ -197,7 +197,8 @@ class DriverDSLImpl(
): CordaFuture<NodeHandle> { ): CordaFuture<NodeHandle> {
val p2pAddress = portAllocation.nextHostAndPort() val p2pAddress = portAllocation.nextHostAndPort()
// TODO: Derive name from the full picked name, don't just wrap the common name // TODO: Derive name from the full picked name, don't just wrap the common name
val name = providedName ?: CordaX500Name("${oneOf(names).organisation}-${p2pAddress.port}", "London", "GB") val name = providedName
?: CordaX500Name("${oneOf(names).organisation}-${p2pAddress.port}", "London", "GB")
val registrationFuture = if (compatibilityZone?.rootCert != null) { val registrationFuture = if (compatibilityZone?.rootCert != null) {
// We don't need the network map to be available to be able to register the node // We don't need the network map to be available to be able to register the node
@ -604,7 +605,8 @@ class DriverDSLImpl(
} }
} }
} }
val p2pReadyFuture = addressMustBeBoundFuture(executorService, config.corda.p2pAddress, process) val effectiveP2PAddress = config.corda.messagingServerAddress ?: config.corda.p2pAddress
val p2pReadyFuture = addressMustBeBoundFuture(executorService, effectiveP2PAddress, process)
return p2pReadyFuture.flatMap { return p2pReadyFuture.flatMap {
val processDeathFuture = poll(executorService, "process death while waiting for RPC (${config.corda.myLegalName})") { val processDeathFuture = poll(executorService, "process death while waiting for RPC (${config.corda.myLegalName})") {
if (process.isAlive) null else process if (process.isAlive) null else process
@ -614,7 +616,7 @@ class DriverDSLImpl(
val networkMapFuture = executorService.fork { visibilityHandle.listen(rpc) }.flatMap { it } val networkMapFuture = executorService.fork { visibilityHandle.listen(rpc) }.flatMap { it }
firstOf(processDeathFuture, networkMapFuture) { firstOf(processDeathFuture, networkMapFuture) {
if (it == processDeathFuture) { if (it == processDeathFuture) {
throw ListenProcessDeathException(config.corda.p2pAddress, process) throw ListenProcessDeathException(effectiveP2PAddress, process)
} }
// Will interrupt polling for process death as this is no longer relevant since the process been // Will interrupt polling for process death as this is no longer relevant since the process been
// successfully started and reflected itself in the NetworkMap. // successfully started and reflected itself in the NetworkMap.
@ -689,6 +691,7 @@ class DriverDSLImpl(
executorService: ScheduledExecutorService, executorService: ScheduledExecutorService,
config: NodeConfig config: NodeConfig
): CordaFuture<Pair<NodeWithInfo, Thread>> { ): CordaFuture<Pair<NodeWithInfo, Thread>> {
val effectiveP2PAddress = config.corda.messagingServerAddress ?: config.corda.p2pAddress
return executorService.fork { return executorService.fork {
log.info("Starting in-process Node ${config.corda.myLegalName.organisation}") log.info("Starting in-process Node ${config.corda.myLegalName.organisation}")
if (!(ManagementFactory.getRuntimeMXBean().inputArguments.any { it.contains("quasar") })) { if (!(ManagementFactory.getRuntimeMXBean().inputArguments.any { it.contains("quasar") })) {
@ -705,7 +708,7 @@ class DriverDSLImpl(
} }
nodeWithInfo to nodeThread nodeWithInfo to nodeThread
}.flatMap { nodeAndThread -> }.flatMap { nodeAndThread ->
addressMustBeBoundFuture(executorService, config.corda.p2pAddress).map { nodeAndThread } addressMustBeBoundFuture(executorService, effectiveP2PAddress).map { nodeAndThread }
} }
} }