
io.axoniq.inspector.client.RSocketInspectorClient.kt Maven / Gradle / Ivy
/*
* Copyright (c) 2022-2023. Inspector Axon
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.axoniq.inspector.client
import io.axoniq.inspector.api.InspectorClientAuthentication
import io.axoniq.inspector.api.InspectorClientIdentifier
import io.axoniq.inspector.AxonInspectorProperties
import io.axoniq.inspector.client.strategy.RSocketPayloadEncodingStrategy
import io.netty.buffer.ByteBufAllocator
import io.netty.buffer.CompositeByteBuf
import io.rsocket.RSocket
import io.rsocket.core.RSocketConnector
import io.rsocket.metadata.*
import io.rsocket.transport.netty.client.TcpClientTransport
import org.axonframework.lifecycle.Lifecycle
import org.axonframework.lifecycle.Phase
import org.slf4j.LoggerFactory
import reactor.core.publisher.Mono
import reactor.netty.tcp.TcpClient
import java.lang.management.ManagementFactory
import java.util.concurrent.ScheduledExecutorService
import java.util.concurrent.ScheduledFuture
import java.util.concurrent.TimeUnit
@Suppress("MemberVisibilityCanBePrivate")
class RSocketInspectorClient(
private val properties: AxonInspectorProperties,
private val setupPayloadCreator: SetupPayloadCreator,
private val registrar: RSocketHandlerRegistrar,
private val encodingStrategy: RSocketPayloadEncodingStrategy,
private val executor: ScheduledExecutorService,
private val nodeName: String = ManagementFactory.getRuntimeMXBean().name,
) : Lifecycle {
private var scheduledReconnector: ScheduledFuture<*>? = null
private val logger = LoggerFactory.getLogger(this::class.java)
private lateinit var rsocket: RSocket
private var connected = false
override fun registerLifecycleHandlers(registry: Lifecycle.LifecycleRegistry) {
registry.onStart(Phase.EXTERNAL_CONNECTIONS, this::start)
registry.onShutdown(Phase.EXTERNAL_CONNECTIONS, this::dispose)
}
fun send(route: String, payload: Any): Mono {
if (!connected) {
return Mono.empty()
}
return rsocket
.requestResponse(encodingStrategy.encode(payload, createRoutingMetadata(route)))
.doOnError {
if (it.message?.contains("Access Denied") == true) {
logger.info("Was unable to send call to Inspector Axon since authentication was incorrect!")
}
}
.then()
}
fun start() {
this.scheduledReconnector = executor.scheduleWithFixedDelay({
if (!connected) {
logger.info("Reconnecting Inspector Axon...")
connect()
}
}, properties.initialDelay, 10000, TimeUnit.MILLISECONDS)
}
fun connect() {
try {
rsocket = createRSocket()
connected = true
} catch (e: Exception) {
logger.info("Failed to connect to Inspector Axon", e)
}
}
private fun createRSocket(): RSocket {
val authentication = InspectorClientAuthentication(
identification = InspectorClientIdentifier(
workspaceId = properties.workspaceId,
environmentId = properties.environmentId,
applicationName = properties.applicationName,
nodeName = nodeName
),
accessToken = properties.accessToken
)
val setupPayload =
encodingStrategy.encode(setupPayloadCreator.createReport(), createSetupMetadata(authentication))
val rsocket = RSocketConnector.create()
.metadataMimeType(WellKnownMimeType.MESSAGE_RSOCKET_COMPOSITE_METADATA.string)
.dataMimeType(encodingStrategy.getMimeType().string)
.setupPayload(setupPayload)
.acceptor { _, rsocket ->
Mono.just(registrar.createRespondingRSocketFor(rsocket))
}
.connect(tcpClientTransport())
.block()!!
return rsocket
}
private fun createRoutingMetadata(route: String): CompositeByteBuf {
val metadata: CompositeByteBuf = ByteBufAllocator.DEFAULT.compositeBuffer()
metadata.addRouteMetadata(route)
return metadata
}
private fun createSetupMetadata(auth: InspectorClientAuthentication): CompositeByteBuf {
val metadata: CompositeByteBuf = ByteBufAllocator.DEFAULT.compositeBuffer()
metadata.addRouteMetadata("client")
metadata.addAuthMetadata(auth)
return metadata
}
private fun tcpClientTransport() =
TcpClientTransport.create(tcpClient())
private fun tcpClient(): TcpClient {
val client = TcpClient.create()
.host(properties.host)
.port(properties.port)
.doOnDisconnected {
connected = false
}
return if (properties.secure) {
return client.secure()
} else client
}
fun isConnected() = connected
fun dispose() {
if (connected) {
rsocket.dispose()
}
scheduledReconnector?.cancel(true)
scheduledReconnector = null
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy