All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jvmMain.no.dossier.libraries.amqpconnector.primitives.AmqpMessage.kt Maven / Gradle / Ivy

package no.dossier.libraries.amqpconnector.primitives

import kotlinx.serialization.KSerializer
import kotlinx.serialization.serializer
import no.dossier.libraries.amqpconnector.error.AmqpConsumingError
import no.dossier.libraries.amqpconnector.serialization.amqpJsonConfig
import no.dossier.libraries.functional.Failure
import no.dossier.libraries.functional.Outcome
import no.dossier.libraries.functional.Success

/* TODO: Reconsider Headers vs Properties naming, maybe there shouldn't be any pre-defined set of Properties at all */
enum class AmqpMessageProperty {
    USER,
    API_KEY,
    REPLY_TO_EXCHANGE
}

enum class AmqpMessageDeliveryMode(val code: Int) {
    TRANSIENT(1),
    PERSISTENT(2);
}

data class AmqpInboundMessage(
    val rawPayload: ByteArray,
    val headers: Map = mapOf(),
    val reply: suspend (
        message: AmqpOutboundMessage<*>,
        replyToExchange: String
    ) -> Unit = { _, _ ->  },
    val acknowledge: suspend () -> Unit = { },
    val reject: suspend () -> Unit = { },
    val replyTo: String? = null,
    val correlationId: String? = null,
    val routingKey: String,
    private val serializer: KSerializer
) {
    val payload: Outcome by lazy {
        try {
            val payload = amqpJsonConfig.decodeFromString(serializer, String(rawPayload))
            Success(payload)
        }
        catch (e: Throwable) {
            Failure(AmqpConsumingError("Unable to deserialize message payload: ${e.message}" +
                    "\n Headers: ${this.headers} \n RoutingKey: ${this.routingKey}"))
        }
    }

    operator fun get(key: AmqpMessageProperty): Outcome =
        headers[key.name]
            ?.let { Success(it) }
            ?: Failure(AmqpConsumingError("Message doesn't contain property: ${key.name}"))

    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (javaClass != other?.javaClass) return false

        other as AmqpInboundMessage<*>

        if (!rawPayload.contentEquals(other.rawPayload)) return false
        if (headers != other.headers) return false
        if (reply != other.reply) return false
        if (acknowledge != other.acknowledge) return false
        if (reject != other.reject) return false
        if (replyTo != other.replyTo) return false
        if (correlationId != other.correlationId) return false
        if (routingKey != other.routingKey) return false
        if (serializer != other.serializer) return false

        return true
    }

    override fun hashCode(): Int {
        var result = rawPayload.contentHashCode()
        result = 31 * result + headers.hashCode()
        result = 31 * result + reply.hashCode()
        result = 31 * result + acknowledge.hashCode()
        result = 31 * result + reject.hashCode()
        result = 31 * result + (replyTo?.hashCode() ?: 0)
        result = 31 * result + (correlationId?.hashCode() ?: 0)
        result = 31 * result + routingKey.hashCode()
        result = 31 * result + serializer.hashCode()
        return result
    }

    override fun toString(): String = """
        AmqpInboundMessage(
          routingKey = $routingKey
          headers = $headers
          replyTo = $replyTo, correlationId = $correlationId
          rawPayload = ${rawPayload.decodeToString()}
        )
    """.trimIndent()
}

/*
* We need this inline factory method because inline constructors are not yet supported in Kotlin
* see https://youtrack.jetbrains.com/issue/KT-30915
*/
@Suppress("FunctionName")
inline fun  AmqpOutboundMessage(
    payload: T,
    headers: Map = mapOf(),
    replyTo: String? = null,
    correlationId: String? = null,
    routingKey: AmqpRoutingKey = AmqpRoutingKey.PublisherDefault,
    deliveryMode: AmqpMessageDeliveryMode = AmqpMessageDeliveryMode.PERSISTENT
): AmqpOutboundMessage = AmqpOutboundMessage(
    payload, headers, replyTo, correlationId, routingKey, serializer(), deliveryMode
)

data class AmqpOutboundMessage(
    val payload: T,
    val headers: Map,
    val replyTo: String?,
    val correlationId: String?,
    val routingKey: AmqpRoutingKey,
    private val serializer: KSerializer,
    val deliveryMode: AmqpMessageDeliveryMode = AmqpMessageDeliveryMode.PERSISTENT
) {

    val rawPayload: ByteArray by lazy { amqpJsonConfig.encodeToString(serializer, payload).toByteArray() }

    operator fun get(key: AmqpMessageProperty): Outcome =
        headers[key.name]
            ?.let { Success(it) }
            ?: Failure(AmqpConsumingError("Message doesn't contain property: ${key.name}"))
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy