-
Notifications
You must be signed in to change notification settings - Fork 27
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
CORE-17009 Manage Multi-Source Event Mediator Consumers and Clients (#…
…4735) Managing consumers, messaging clients and message router for Multi-Source Event Mediator: - creating them before it starting to process messages - recreating them to recover from errors (when needed, based on error type) - closing them
- Loading branch information
Showing
3 changed files
with
330 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
80 changes: 80 additions & 0 deletions
80
...ing-impl/src/main/kotlin/net/corda/messaging/mediator/factory/MediatorComponentFactory.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
package net.corda.messaging.mediator.factory | ||
|
||
import net.corda.messaging.api.mediator.MediatorConsumer | ||
import net.corda.messaging.api.mediator.MessageRouter | ||
import net.corda.messaging.api.mediator.MessagingClient | ||
import net.corda.messaging.api.mediator.config.MediatorConsumerConfig | ||
import net.corda.messaging.api.mediator.config.MessagingClientConfig | ||
import net.corda.messaging.api.mediator.factory.MediatorConsumerFactory | ||
import net.corda.messaging.api.mediator.factory.MessageRouterFactory | ||
import net.corda.messaging.api.mediator.factory.MessagingClientFactory | ||
import net.corda.messaging.api.processor.StateAndEventProcessor | ||
|
||
/** | ||
* Factory for creating various components used by Multi-Source Event Mediator. | ||
*/ | ||
internal class MediatorComponentFactory<K : Any, S : Any, E : Any>( | ||
private val messageProcessor: StateAndEventProcessor<K, S, E>, | ||
private val consumerFactories: Collection<MediatorConsumerFactory>, | ||
private val clientFactories: Collection<MessagingClientFactory>, | ||
private val messageRouterFactory: MessageRouterFactory, | ||
) { | ||
|
||
/** | ||
* Creates message consumers. | ||
* | ||
* @param onSerializationError Function for handling serialization errors. | ||
* @return List of created [MediatorConsumer]s. | ||
*/ | ||
fun createConsumers( | ||
onSerializationError: (ByteArray) -> Unit | ||
): List<MediatorConsumer<K, E>> { | ||
check(consumerFactories.isNotEmpty()) { | ||
"No consumer factory set in configuration" | ||
} | ||
return consumerFactories.map { consumerFactory -> | ||
consumerFactory.create( | ||
MediatorConsumerConfig( | ||
messageProcessor.keyClass, | ||
messageProcessor.eventValueClass, | ||
onSerializationError | ||
) | ||
) | ||
} | ||
} | ||
|
||
/** | ||
* Creates messaging clients. | ||
* | ||
* @param onSerializationError Function for handling serialization errors. | ||
* @return List of created [MessagingClient]s. | ||
*/ | ||
fun createClients( | ||
onSerializationError: (ByteArray) -> Unit | ||
): List<MessagingClient> { | ||
check(clientFactories.isNotEmpty()) { | ||
"No client factory set in configuration" | ||
} | ||
return clientFactories.map { clientFactory -> | ||
clientFactory.create( | ||
MessagingClientConfig(onSerializationError) | ||
) | ||
} | ||
} | ||
|
||
/** | ||
* Creates message router. | ||
* | ||
* @param clients Collection of [MessagingClient]s. | ||
* @return Message router. | ||
*/ | ||
fun createRouter( | ||
clients: Collection<MessagingClient> | ||
): MessageRouter { | ||
val clientsById = clients.associateBy { it.id } | ||
return messageRouterFactory.create { id -> | ||
clientsById[id] | ||
?: throw IllegalStateException("Messaging client with ID \"$id\" not found") | ||
} | ||
} | ||
} |
167 changes: 167 additions & 0 deletions
167
...impl/src/test/kotlin/net/corda/messaging/mediator/factory/MediatorComponentFactoryTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
package net.corda.messaging.mediator.factory | ||
|
||
import net.corda.messaging.api.mediator.MediatorConsumer | ||
import net.corda.messaging.api.mediator.MessageRouter | ||
import net.corda.messaging.api.mediator.MessagingClient | ||
import net.corda.messaging.api.mediator.config.MediatorConsumerConfig | ||
import net.corda.messaging.api.mediator.config.MessagingClientConfig | ||
import net.corda.messaging.api.mediator.factory.MediatorConsumerFactory | ||
import net.corda.messaging.api.mediator.factory.MessageRouterFactory | ||
import net.corda.messaging.api.mediator.factory.MessagingClientFactory | ||
import net.corda.messaging.api.mediator.factory.MessagingClientFinder | ||
import net.corda.messaging.api.processor.StateAndEventProcessor | ||
import net.corda.messaging.api.records.Record | ||
import org.junit.jupiter.api.Assertions.assertEquals | ||
import org.junit.jupiter.api.Assertions.assertNotNull | ||
import org.junit.jupiter.api.BeforeEach | ||
import org.junit.jupiter.api.Test | ||
import org.junit.jupiter.api.assertThrows | ||
import org.mockito.Mockito | ||
import org.mockito.kotlin.any | ||
import org.mockito.kotlin.argumentCaptor | ||
import org.mockito.kotlin.doReturn | ||
import org.mockito.kotlin.mock | ||
import org.mockito.kotlin.verify | ||
import org.mockito.kotlin.whenever | ||
|
||
class MediatorComponentFactoryTest { | ||
private lateinit var mediatorComponentFactory: MediatorComponentFactory<String, String, String> | ||
private val messageProcessor = object : StateAndEventProcessor<String, String, String> { | ||
override fun onNext(state: String?, event: Record<String, String>): StateAndEventProcessor.Response<String> { | ||
TODO("Not yet implemented") | ||
} | ||
override val keyClass get() = String::class.java | ||
override val stateValueClass get() = String::class.java | ||
override val eventValueClass get() = String::class.java | ||
|
||
} | ||
private val consumerFactories = listOf( | ||
mock<MediatorConsumerFactory>(), | ||
mock<MediatorConsumerFactory>(), | ||
) | ||
private val clientFactories = listOf( | ||
mock<MessagingClientFactory>(), | ||
mock<MessagingClientFactory>(), | ||
) | ||
private val messageRouterFactory = mock<MessageRouterFactory>() | ||
|
||
@BeforeEach | ||
fun beforeEach() { | ||
consumerFactories.forEach { | ||
doReturn(mock<MediatorConsumer<String, String>>()).`when`(it).create( | ||
any<MediatorConsumerConfig<String, String>>() | ||
) | ||
} | ||
|
||
clientFactories.forEach { | ||
doReturn(mock<MessagingClient>()).`when`(it).create( | ||
any<MessagingClientConfig>() | ||
) | ||
} | ||
|
||
doReturn(mock<MessageRouter>()).`when`(messageRouterFactory).create( | ||
any<MessagingClientFinder>() | ||
) | ||
|
||
mediatorComponentFactory = MediatorComponentFactory( | ||
messageProcessor, | ||
consumerFactories, | ||
clientFactories, | ||
messageRouterFactory, | ||
) | ||
} | ||
|
||
@Test | ||
fun `successfully creates consumers`() { | ||
val onSerializationError: (ByteArray) -> Unit = {} | ||
|
||
val mediatorConsumers = mediatorComponentFactory.createConsumers(onSerializationError) | ||
|
||
assertEquals(consumerFactories.size, mediatorConsumers.size) | ||
mediatorConsumers.forEach { | ||
assertNotNull(it) | ||
} | ||
|
||
consumerFactories.forEach { | ||
val consumerConfigCaptor = argumentCaptor<MediatorConsumerConfig<String, String>>() | ||
verify(it).create(consumerConfigCaptor.capture()) | ||
val consumerConfig = consumerConfigCaptor.firstValue | ||
assertEquals(String::class.java, consumerConfig.keyClass) | ||
assertEquals(String::class.java, consumerConfig.valueClass) | ||
assertEquals(onSerializationError, consumerConfig.onSerializationError) | ||
} | ||
} | ||
|
||
@Test | ||
fun `throws exception when consumer factory not provided`() { | ||
val mediatorComponentFactory = MediatorComponentFactory( | ||
messageProcessor, | ||
emptyList(), | ||
clientFactories, | ||
messageRouterFactory, | ||
) | ||
|
||
assertThrows<IllegalStateException> { | ||
mediatorComponentFactory.createConsumers { } | ||
} | ||
} | ||
|
||
@Test | ||
fun `successfully creates clients`() { | ||
val onSerializationError: (ByteArray) -> Unit = {} | ||
|
||
val mediatorClients = mediatorComponentFactory.createClients(onSerializationError) | ||
|
||
assertEquals(clientFactories.size, mediatorClients.size) | ||
mediatorClients.forEach { | ||
assertNotNull(it) | ||
} | ||
|
||
clientFactories.forEach { | ||
val clientConfigCaptor = argumentCaptor<MessagingClientConfig>() | ||
verify(it).create(clientConfigCaptor.capture()) | ||
val clientConfig = clientConfigCaptor.firstValue | ||
assertEquals(onSerializationError, clientConfig.onSerializationError) | ||
} | ||
} | ||
|
||
@Test | ||
fun `throws exception when client factory not provided`() { | ||
val mediatorComponentFactory = MediatorComponentFactory( | ||
messageProcessor, | ||
consumerFactories, | ||
emptyList(), | ||
messageRouterFactory, | ||
) | ||
|
||
assertThrows<IllegalStateException> { | ||
mediatorComponentFactory.createClients { } | ||
} | ||
} | ||
|
||
@Test | ||
fun `successfully creates message router`() { | ||
val clients = listOf( | ||
mock<MessagingClient>(), | ||
mock<MessagingClient>(), | ||
) | ||
clients.forEachIndexed { id, client -> | ||
Mockito.doReturn(id.toString()).whenever(client).id | ||
} | ||
|
||
val messageRouter = mediatorComponentFactory.createRouter(clients) | ||
|
||
assertNotNull(messageRouter) | ||
|
||
val messagingClientFinderCaptor = argumentCaptor<MessagingClientFinder>() | ||
verify(messageRouterFactory).create(messagingClientFinderCaptor.capture()) | ||
val messagingClientFinder = messagingClientFinderCaptor.firstValue | ||
|
||
clients.forEachIndexed { id, client -> | ||
assertEquals(client, messagingClientFinder.find(id.toString())) | ||
} | ||
assertThrows<IllegalStateException> { | ||
messagingClientFinder.find("unknownId") | ||
} | ||
} | ||
} |