mirror of
https://github.com/corda/corda.git
synced 2025-06-18 15:18:16 +00:00
Break down topic into component parts
Break down what is referred to as "topic" of a message into its component parts. This splits the general topic from the session ID, so it's clear where a session ID is provided, and whether any given topic string includes a session ID or not.
This commit is contained in:
@ -1,6 +1,7 @@
|
||||
package com.r3corda.core.messaging
|
||||
|
||||
import com.google.common.util.concurrent.ListenableFuture
|
||||
import com.r3corda.core.node.services.DEFAULT_SESSION_ID
|
||||
import com.r3corda.core.serialization.DeserializeAsKotlinObjectDef
|
||||
import com.r3corda.core.serialization.serialize
|
||||
import java.time.Instant
|
||||
@ -22,7 +23,7 @@ import javax.annotation.concurrent.ThreadSafe
|
||||
interface MessagingService {
|
||||
/**
|
||||
* The provided function will be invoked for each received message whose topic matches the given string, on the given
|
||||
* executor. The topic can be the empty string to match all messages.
|
||||
* executor.
|
||||
*
|
||||
* If no executor is received then the callback will run on threads provided by the messaging service, and the
|
||||
* callback is expected to be thread safe as a result.
|
||||
@ -30,8 +31,28 @@ interface MessagingService {
|
||||
* The returned object is an opaque handle that may be used to un-register handlers later with [removeMessageHandler].
|
||||
* The handle is passed to the callback as well, to avoid race conditions whereby the callback wants to unregister
|
||||
* itself and yet addMessageHandler hasn't returned the handle yet.
|
||||
*
|
||||
* @param topic identifier for the general subject of the message, for example "platform.network_map.fetch".
|
||||
* The topic can be the empty string to match all messages (session ID must be [DEFAULT_SESSION_ID]).
|
||||
* @param sessionID identifier for the session the message is part of. For services listening before
|
||||
* a session is established, use [DEFAULT_SESSION_ID].
|
||||
*/
|
||||
fun addMessageHandler(topic: String = "", executor: Executor? = null, callback: (Message, MessageHandlerRegistration) -> Unit): MessageHandlerRegistration
|
||||
fun addMessageHandler(topic: String = "", sessionID: Long = DEFAULT_SESSION_ID, executor: Executor? = null, callback: (Message, MessageHandlerRegistration) -> Unit): MessageHandlerRegistration
|
||||
|
||||
/**
|
||||
* The provided function will be invoked for each received message whose topic and session matches, on the
|
||||
* given executor.
|
||||
*
|
||||
* If no executor is received then the callback will run on threads provided by the messaging service, and the
|
||||
* callback is expected to be thread safe as a result.
|
||||
*
|
||||
* The returned object is an opaque handle that may be used to un-register handlers later with [removeMessageHandler].
|
||||
* The handle is passed to the callback as well, to avoid race conditions whereby the callback wants to unregister
|
||||
* itself and yet addMessageHandler hasn't returned the handle yet.
|
||||
*
|
||||
* @param topicSession identifier for the topic and session to listen for messages arriving on.
|
||||
*/
|
||||
fun addMessageHandler(topicSession: TopicSession, executor: Executor? = null, callback: (Message, MessageHandlerRegistration) -> Unit): MessageHandlerRegistration
|
||||
|
||||
/**
|
||||
* Removes a handler given the object returned from [addMessageHandler]. The callback will no longer be invoked once
|
||||
@ -55,34 +76,81 @@ interface MessagingService {
|
||||
|
||||
/**
|
||||
* Returns an initialised [Message] with the current time, etc, already filled in.
|
||||
*
|
||||
* @param topic identifier for the general subject of the message, for example "platform.network_map.fetch".
|
||||
* Must not be blank.
|
||||
* @param sessionID identifier for the session the message is part of. For messages sent to services before the
|
||||
* construction of a session, use [DEFAULT_SESSION_ID].
|
||||
*/
|
||||
fun createMessage(topic: String, data: ByteArray): Message
|
||||
fun createMessage(topic: String, sessionID: Long = DEFAULT_SESSION_ID, data: ByteArray): Message
|
||||
|
||||
/**
|
||||
* Returns an initialised [Message] with the current time, etc, already filled in.
|
||||
*
|
||||
* @param topicSession identifier for the topic and session the message is sent to.
|
||||
*/
|
||||
fun createMessage(topicSession: TopicSession, data: ByteArray): Message
|
||||
|
||||
/** Returns an address that refers to this node. */
|
||||
val myAddress: SingleMessageRecipient
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a handler for the given topic that runs the given callback with the message and then removes itself. This
|
||||
* is useful for one-shot handlers that aren't supposed to stick around permanently. Note that this callback doesn't
|
||||
* take the registration object, unlike the callback to [MessagingService.addMessageHandler].
|
||||
* Registers a handler for the given topic and session ID that runs the given callback with the message and then removes
|
||||
* itself. This is useful for one-shot handlers that aren't supposed to stick around permanently. Note that this callback
|
||||
* doesn't take the registration object, unlike the callback to [MessagingService.addMessageHandler], as the handler is
|
||||
* automatically deregistered before the callback runs.
|
||||
*
|
||||
* @param topic identifier for the general subject of the message, for example "platform.network_map.fetch".
|
||||
* The topic can be the empty string to match all messages (session ID must be [DEFAULT_SESSION_ID]).
|
||||
* @param sessionID identifier for the session the message is part of. For services listening before
|
||||
* a session is established, use [DEFAULT_SESSION_ID].
|
||||
*/
|
||||
fun MessagingService.runOnNextMessage(topic: String = "", executor: Executor? = null, callback: (Message) -> Unit) {
|
||||
fun MessagingService.runOnNextMessage(topic: String, sessionID: Long, executor: Executor? = null, callback: (Message) -> Unit)
|
||||
= runOnNextMessage(TopicSession(topic, sessionID), executor, callback)
|
||||
|
||||
/**
|
||||
* Registers a handler for the given topic and session that runs the given callback with the message and then removes
|
||||
* itself. This is useful for one-shot handlers that aren't supposed to stick around permanently. Note that this callback
|
||||
* doesn't take the registration object, unlike the callback to [MessagingService.addMessageHandler].
|
||||
*
|
||||
* @param topicSession identifier for the topic and session to listen for messages arriving on.
|
||||
*/
|
||||
fun MessagingService.runOnNextMessage(topicSession: TopicSession, executor: Executor? = null, callback: (Message) -> Unit) {
|
||||
val consumed = AtomicBoolean()
|
||||
addMessageHandler(topic, executor) { msg, reg ->
|
||||
addMessageHandler(topicSession, executor) { msg, reg ->
|
||||
removeMessageHandler(reg)
|
||||
check(!consumed.getAndSet(true)) { "Called more than once" }
|
||||
check(msg.topic == topic) { "Topic mismatch: ${msg.topic} vs $topic" }
|
||||
check(msg.topicSession == topicSession) { "Topic/session mismatch: ${msg.topicSession} vs $topicSession" }
|
||||
callback(msg)
|
||||
}
|
||||
}
|
||||
|
||||
fun MessagingService.send(topic: String, payload: Any, to: MessageRecipients) {
|
||||
send(createMessage(topic, payload.serialize().bits), to)
|
||||
}
|
||||
fun MessagingService.send(topic: String, sessionID: Long, payload: Any, to: MessageRecipients)
|
||||
= send(TopicSession(topic, sessionID), payload, to)
|
||||
|
||||
fun MessagingService.send(topicSession: TopicSession, payload: Any, to: MessageRecipients)
|
||||
= send(createMessage(topicSession, payload.serialize().bits), to)
|
||||
|
||||
interface MessageHandlerRegistration
|
||||
|
||||
/**
|
||||
* An identifier for the endpoint [MessagingService] message handlers listen at.
|
||||
*
|
||||
* @param topic identifier for the general subject of the message, for example "platform.network_map.fetch".
|
||||
* The topic can be the empty string to match all messages (session ID must be [DEFAULT_SESSION_ID]).
|
||||
* @param sessionID identifier for the session the message is part of. For services listening before
|
||||
* a session is established, use [DEFAULT_SESSION_ID].
|
||||
*/
|
||||
data class TopicSession(val topic: String, val sessionID: Long = DEFAULT_SESSION_ID) {
|
||||
companion object {
|
||||
val Blank = TopicSession("", DEFAULT_SESSION_ID)
|
||||
}
|
||||
fun isBlank() = topic.isBlank() && sessionID == DEFAULT_SESSION_ID
|
||||
|
||||
override fun toString(): String = "${topic}.${sessionID}"
|
||||
}
|
||||
|
||||
/**
|
||||
* A message is defined, at this level, to be a (topic, timestamp, byte arrays) triple, where the topic is a string in
|
||||
* Java-style reverse dns form, with "platform." being a prefix reserved by the platform for its own use. Vendor
|
||||
@ -94,7 +162,7 @@ interface MessageHandlerRegistration
|
||||
* the timestamp field they probably will be, even if an implementation just uses a hash prefix as the message id.
|
||||
*/
|
||||
interface Message {
|
||||
val topic: String
|
||||
val topicSession: TopicSession
|
||||
val data: ByteArray
|
||||
val debugTimestamp: Instant
|
||||
val debugMessageID: String
|
||||
|
@ -10,9 +10,10 @@ import java.security.PrivateKey
|
||||
import java.security.PublicKey
|
||||
|
||||
/**
|
||||
* Postfix for base topics when sending a request to a service.
|
||||
* Session ID to use for services listening for the first message in a session (before a
|
||||
* specific session ID has been established).
|
||||
*/
|
||||
val TOPIC_DEFAULT_POSTFIX = ".0"
|
||||
val DEFAULT_SESSION_ID = 0L
|
||||
|
||||
/**
|
||||
* This file defines various 'services' which are not currently fleshed out. A service is a module that provides
|
||||
|
@ -7,6 +7,7 @@ import com.r3corda.core.crypto.DigitalSignature
|
||||
import com.r3corda.core.crypto.Party
|
||||
import com.r3corda.core.crypto.signWithECDSA
|
||||
import com.r3corda.core.node.NodeInfo
|
||||
import com.r3corda.core.node.services.DEFAULT_SESSION_ID
|
||||
import com.r3corda.core.protocols.ProtocolLogic
|
||||
import com.r3corda.core.random63BitValue
|
||||
import com.r3corda.core.seconds
|
||||
@ -157,7 +158,7 @@ object TwoPartyDealProtocol {
|
||||
// Copy the transaction to every regulator in the network. This is obviously completely bogus, it's
|
||||
// just for demo purposes.
|
||||
for (regulator in regulators) {
|
||||
send(regulator.identity, 0, fullySigned)
|
||||
send(regulator.identity, DEFAULT_SESSION_ID, fullySigned)
|
||||
}
|
||||
}
|
||||
|
||||
@ -461,7 +462,7 @@ object TwoPartyDealProtocol {
|
||||
val initation = FixingSessionInitiation(sessionID, sortedParties[0], serviceHub.storageService.myLegalIdentity, timeout)
|
||||
|
||||
// Send initiation to other side to launch one side of the fixing protocol (the Fixer).
|
||||
send(sortedParties[1], 0, initation)
|
||||
send(sortedParties[1], DEFAULT_SESSION_ID, initation)
|
||||
|
||||
// Then start the other side of the fixing protocol.
|
||||
val protocol = Floater(ref, sessionID)
|
||||
|
Reference in New Issue
Block a user