From 29b847327a0183189cea3da1c539a351fe5ba9ec Mon Sep 17 00:00:00 2001 From: belljun3395 <195850@jnu.ac.kr> Date: Fri, 20 Dec 2024 23:13:33 +0900 Subject: [PATCH 01/11] =?UTF-8?q?feat:=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=20?= =?UTF-8?q?=EB=AA=A8=EB=93=88=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- event/build.gradle.kts | 11 +++++++++++ event/src/main/kotlin/event/config/EventConfig.kt | 13 +++++++++++++ settings.gradle.kts | 3 ++- 3 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 event/build.gradle.kts create mode 100644 event/src/main/kotlin/event/config/EventConfig.kt diff --git a/event/build.gradle.kts b/event/build.gradle.kts new file mode 100644 index 000000000..fc6abc449 --- /dev/null +++ b/event/build.gradle.kts @@ -0,0 +1,11 @@ +tasks.getByName("bootJar") { + enabled = false +} + +tasks.getByName("jar") { + enabled = true +} + +dependencies { + implementation("org.springframework.boot:spring-boot-starter-json") +} \ No newline at end of file diff --git a/event/src/main/kotlin/event/config/EventConfig.kt b/event/src/main/kotlin/event/config/EventConfig.kt new file mode 100644 index 000000000..a322a7a8d --- /dev/null +++ b/event/src/main/kotlin/event/config/EventConfig.kt @@ -0,0 +1,13 @@ +package event.config + +import org.springframework.context.annotation.ComponentScan +import org.springframework.context.annotation.Configuration + +@Configuration +@ComponentScan(basePackages = [EventConfig.BASE_PACKAGE]) +class EventConfig { + companion object { + const val BASE_PACKAGE = "event" + const val BEAN_NAME_PREFIX = "event" + } +} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 0bfcbc466..a33598d3d 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -6,4 +6,5 @@ include("repo") include("email") include("storage") include("web") -include("security") \ No newline at end of file +include("security") +include("event") \ No newline at end of file From 5d186b7a3fe4616d32497824f0726b2c49cb6f3d Mon Sep 17 00:00:00 2001 From: belljun3395 <195850@jnu.ac.kr> Date: Fri, 20 Dec 2024 23:14:20 +0900 Subject: [PATCH 02/11] =?UTF-8?q?feat:=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- event/src/main/kotlin/event/Event.kt | 23 ++++++ event/src/main/kotlin/event/EventDetails.kt | 14 ++++ event/src/main/kotlin/event/EventHandler.kt | 18 +++++ event/src/main/kotlin/event/EventRePlayer.kt | 15 ++++ event/src/main/kotlin/event/EventUtils.kt | 26 ++++++ event/src/main/kotlin/event/TimeOutEvent.kt | 83 ++++++++++++++++++++ 6 files changed, 179 insertions(+) create mode 100644 event/src/main/kotlin/event/Event.kt create mode 100644 event/src/main/kotlin/event/EventDetails.kt create mode 100644 event/src/main/kotlin/event/EventHandler.kt create mode 100644 event/src/main/kotlin/event/EventRePlayer.kt create mode 100644 event/src/main/kotlin/event/EventUtils.kt create mode 100644 event/src/main/kotlin/event/TimeOutEvent.kt diff --git a/event/src/main/kotlin/event/Event.kt b/event/src/main/kotlin/event/Event.kt new file mode 100644 index 000000000..28e3ab5d5 --- /dev/null +++ b/event/src/main/kotlin/event/Event.kt @@ -0,0 +1,23 @@ +package event + +/** + * Event + * + * 이벤트 + * + * @property eventId 이벤트 식별자 + * @property eventType 이벤트 행위 타입 + * @property eventTime 이벤트 발행 시간 (기본값: 현재 시간) + */ +abstract class Event( + protected val eventId: String = EventUtils.generateEventId(), + protected val eventType: String, + protected val eventTime: Long = System.currentTimeMillis(), +) { + /** + * Get data + * + * @return 이벤트 데이터 + */ + abstract fun getData(): Map +} \ No newline at end of file diff --git a/event/src/main/kotlin/event/EventDetails.kt b/event/src/main/kotlin/event/EventDetails.kt new file mode 100644 index 000000000..56352f2fb --- /dev/null +++ b/event/src/main/kotlin/event/EventDetails.kt @@ -0,0 +1,14 @@ +package event + +/** + * Event details + * + * 이벤트 상세 정보 + * + * @property outBox 이벤트 외부 발행 여부 + */ +@Target(AnnotationTarget.CLASS) +@Retention(AnnotationRetention.RUNTIME) +annotation class EventDetails( + val outBox: Boolean = false, +) \ No newline at end of file diff --git a/event/src/main/kotlin/event/EventHandler.kt b/event/src/main/kotlin/event/EventHandler.kt new file mode 100644 index 000000000..78c1e93e7 --- /dev/null +++ b/event/src/main/kotlin/event/EventHandler.kt @@ -0,0 +1,18 @@ +package event + +/** + * Event handler + * + * 이벤트 핸들러 + * + * @param T Event 클래스를 상속 받은 클래스 + * @see Event + */ +interface EventHandler { + /** + * Handle event + * + * @param event 이벤트 + */ + fun handle(event: T) +} \ No newline at end of file diff --git a/event/src/main/kotlin/event/EventRePlayer.kt b/event/src/main/kotlin/event/EventRePlayer.kt new file mode 100644 index 000000000..a81aa23eb --- /dev/null +++ b/event/src/main/kotlin/event/EventRePlayer.kt @@ -0,0 +1,15 @@ +package event + +/** + * Event RePlayer + * + * 이벤트 재생기 + */ +abstract class EventRePlayer { + /** + * Replay + * + * 정상 처리 되지 않은 이벤트를 재생합니다. + */ + abstract fun replay() +} \ No newline at end of file diff --git a/event/src/main/kotlin/event/EventUtils.kt b/event/src/main/kotlin/event/EventUtils.kt new file mode 100644 index 000000000..941afbb49 --- /dev/null +++ b/event/src/main/kotlin/event/EventUtils.kt @@ -0,0 +1,26 @@ +package event + +import java.util.* + +/** + * Is out box extension for Event Class + * + * 이벤트 외부 발행 여부를 확인합니다. + * + * @return 이벤트 외부 발행 여부 + * @see Event + */ +fun Event.isOutBox(): Boolean = this::class.annotations.any { annotation -> annotation is EventDetails && annotation.outBox } + +class EventUtils { + companion object { + /** + * Generate event id + * + * 이벤트 ID를 생성합니다. + * + * @return 이벤트 ID + */ + fun generateEventId(): String = UUID.randomUUID().toString() + } +} \ No newline at end of file diff --git a/event/src/main/kotlin/event/TimeOutEvent.kt b/event/src/main/kotlin/event/TimeOutEvent.kt new file mode 100644 index 000000000..77bcd4255 --- /dev/null +++ b/event/src/main/kotlin/event/TimeOutEvent.kt @@ -0,0 +1,83 @@ +package event + +import org.springframework.context.ApplicationEventPublisher + +/** + * Time out event + * + * 시간이 설정된 이벤트 + * + * @param expiredTime 만료 시간 + * @param completed 완료 여부(기본값: false) + * @param eventPublisher 이벤트 발행자 + * + * @see TimeExpiredEvent 시간 만료 이벤트 + */ +abstract class TimeOutEvent( + eventId: String = EventUtils.generateEventId(), + eventType: String, + eventTime: Long = System.currentTimeMillis(), + protected val expiredTime: Long, + protected var completed: Boolean = false, + protected val eventPublisher: ApplicationEventPublisher, +) : Event( + eventId, + eventType, + eventTime, + ), + Runnable { + /** + * Complete event + * + * 이벤트 완료 처리 + */ + fun complete() { + completed = true + } + + /** + * Is expired + * + * @param time 시간 (기본값: 현재 시간) + * @return 이벤트 만료 여부 + */ + fun isExpired(time: Long = System.currentTimeMillis()): Boolean = !completed && time > expiredTime + + /** + * Run + * + * 이벤트 만료시 실행 + */ + override fun run() { + publishExpiredTimeEvent() + } + + /** + * Publish expired time event + * + * 시간 만료 이벤트 발행 + */ + private fun publishExpiredTimeEvent() { + eventPublisher.publishEvent(timeExpiredEvent()) + } + + /** + * Expired time event + * + * 시간 만료 이벤트 + */ + abstract fun timeExpiredEvent(): TimeExpiredEvent +} + +/** + * Expired time event + */ +abstract class TimeExpiredEvent( + eventId: String, + eventType: String, + eventTime: Long, +) : Event( + eventId, + eventType, + eventTime, + ) \ No newline at end of file From 22cb97ecbed252fe911ec3261c8fc8fac515e585 Mon Sep 17 00:00:00 2001 From: belljun3395 <195850@jnu.ac.kr> Date: Fri, 20 Dec 2024 23:14:48 +0900 Subject: [PATCH 03/11] =?UTF-8?q?feat:=20=EB=A9=94=EC=8B=9C=EC=A7=80=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/kotlin/event/message/Message.kt | 30 ++++++++++++ .../kotlin/event/message/MessageMapper.kt | 27 +++++++++++ .../kotlin/event/message/MessagePayload.kt | 26 +++++++++++ .../main/kotlin/event/message/MessageRelay.kt | 18 ++++++++ .../event/message/MessageReverseRelay.kt | 46 +++++++++++++++++++ .../kotlin/event/message/MessageSender.kt | 17 +++++++ 6 files changed, 164 insertions(+) create mode 100644 event/src/main/kotlin/event/message/Message.kt create mode 100644 event/src/main/kotlin/event/message/MessageMapper.kt create mode 100644 event/src/main/kotlin/event/message/MessagePayload.kt create mode 100644 event/src/main/kotlin/event/message/MessageRelay.kt create mode 100644 event/src/main/kotlin/event/message/MessageReverseRelay.kt create mode 100644 event/src/main/kotlin/event/message/MessageSender.kt diff --git a/event/src/main/kotlin/event/message/Message.kt b/event/src/main/kotlin/event/message/Message.kt new file mode 100644 index 000000000..17c9e4174 --- /dev/null +++ b/event/src/main/kotlin/event/message/Message.kt @@ -0,0 +1,30 @@ +package event.message + +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty + +/** + * Message + * + * 외부 시스템과의 통신을 위한 메시지 + * + * @property payload 메시지 페이로드 + */ +abstract class Message + @JsonCreator + constructor( + @JsonProperty("payload") var payload: MessagePayload?, + ) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || this::class != other::class) return false + + other as Message + + if (payload != other.payload) return false + + return true + } + + override fun hashCode(): Int = payload?.hashCode() ?: 0 + } \ No newline at end of file diff --git a/event/src/main/kotlin/event/message/MessageMapper.kt b/event/src/main/kotlin/event/message/MessageMapper.kt new file mode 100644 index 000000000..959c64355 --- /dev/null +++ b/event/src/main/kotlin/event/message/MessageMapper.kt @@ -0,0 +1,27 @@ +package event.message + +import event.Event +import java.util.Optional + +/** + * Message mapper + * + * 이벤트를 메시지로 변환하기 위한 매퍼 + * + * @param T Event 클래스를 상속 받은 클래스 + * @param R Message 클래스를 상속 받은 클래스 + * + * @see Event + * @see Message + */ +abstract class MessageMapper { + /** + * Map + * + * 이벤트를 메시지로 변환 + * + * @param event 이벤트 + * @return Optional 메시지 + */ + abstract fun map(event: T): Optional +} \ No newline at end of file diff --git a/event/src/main/kotlin/event/message/MessagePayload.kt b/event/src/main/kotlin/event/message/MessagePayload.kt new file mode 100644 index 000000000..f9bf135a5 --- /dev/null +++ b/event/src/main/kotlin/event/message/MessagePayload.kt @@ -0,0 +1,26 @@ +package event.message + +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty + +/** + * Message payload + * + * 메시지 페이로드 + * + * @property eventId 이벤트 식별자 + * @property eventType 이벤트 행위 타입 + * @property eventTime 이벤트 발행 시간 + * @property data 이벤트 데이터 + * + * @see event.Event + */ + +data class MessagePayload + @JsonCreator + constructor( + @JsonProperty("eventId") val eventId: String?, + @JsonProperty("eventType") val eventType: String?, + @JsonProperty("eventTime") val eventTime: Long?, + @JsonProperty("data") val data: Map?, + ) \ No newline at end of file diff --git a/event/src/main/kotlin/event/message/MessageRelay.kt b/event/src/main/kotlin/event/message/MessageRelay.kt new file mode 100644 index 000000000..dc342a04a --- /dev/null +++ b/event/src/main/kotlin/event/message/MessageRelay.kt @@ -0,0 +1,18 @@ +package event.message + +/** + * Message relay + * + * 내부 이벤트를 외부에 메시지로 전달하기 위한 메시지 릴레이 + * + * @param T Message 클래스를 상속 받은 클래스 + * @see MessageSender 메시지를 전달하기 위한 메시지 발신자 + */ +interface MessageRelay { + /** + * Publish + * + * @param message 외부로 전달할 메시지 + */ + fun publish(message: T) +} \ No newline at end of file diff --git a/event/src/main/kotlin/event/message/MessageReverseRelay.kt b/event/src/main/kotlin/event/message/MessageReverseRelay.kt new file mode 100644 index 000000000..b88302c6d --- /dev/null +++ b/event/src/main/kotlin/event/message/MessageReverseRelay.kt @@ -0,0 +1,46 @@ +package event.message + +import event.Event + +/** + * Message reverse relay + * + * 외부 시스템에서 전달 받은 메시지를 내부에 이벤트로 전달하기 위한 메시지 리버스 릴레이 + * + * `Bar 메시지를 수신하여 Foo 이벤트로 변환하여 내부로 전달하는 예시` + * + * ```kotlin + * class FooBarLocalMessageReverseRelay( + * private val messageSender: MessageSender, + * private val messageMapper: MessageMapper, + * private val eventPublisher: ApplicationEventPublisher, +* ) : MessageReverseRelay { + * + * override fun publish(event: FooEvent) { + * applicationEventPublisher.publishEvent(event) + * } + * + * @EventListener + * @LocalSubscribeMessage(topic ="bar") + * fun onApplicationEvent(message: BarMessage) { + * messageMapper.map(message).ifPresent { publish(it) } + * } + * } + * ``` + * @param T Event 클래스를 상속 받은 클래스 + * + * @see MessageSender 메시지를 전달하기 위한 메시지 발신자 + * @see MessageMapper 메시지를 이벤트로 변환하기 위한 메시지 매퍼 + * @see event.message.local.LocalSubscribeMessage 내부 메시지 수신을 위한 어노테이션 + * + * @see org.springframework.context.event.EventListener 이벤트 리스너 (기본 설정 메서드 명: onApplicationEvent) + * @see org.springframework.context.ApplicationEventPublisher + */ +interface MessageReverseRelay { + /** + * Publish + * + * @param event 내부로 전달할 이벤트 + */ + fun publish(event: T) +} \ No newline at end of file diff --git a/event/src/main/kotlin/event/message/MessageSender.kt b/event/src/main/kotlin/event/message/MessageSender.kt new file mode 100644 index 000000000..81193975e --- /dev/null +++ b/event/src/main/kotlin/event/message/MessageSender.kt @@ -0,0 +1,17 @@ +package event.message + +/** + * Message sender + * + * 외부 시스템으로 메시지를 전달하기 위한 메시지 발신자 + * + * @param T Message 클래스를 상속 받은 클래스 + */ +interface MessageSender { + /** + * Send + * + * @param message 전달할 메시지 + */ + fun send(message: T) +} \ No newline at end of file From ebf3d5434d4bc6f9d63756bc90ff891ee1c3e743 Mon Sep 17 00:00:00 2001 From: belljun3395 <195850@jnu.ac.kr> Date: Fri, 20 Dec 2024 23:15:35 +0900 Subject: [PATCH 04/11] =?UTF-8?q?feat:=20LocalMessage=20=ED=99=98=EA=B2=BD?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../message/local/LocalMessageBroadCaster.kt | 50 +++++++++++++++++++ .../event/message/local/LocalMessageConfig.kt | 48 ++++++++++++++++++ .../message/local/LocalSubscribeMessage.kt | 14 ++++++ 3 files changed, 112 insertions(+) create mode 100644 event/src/main/kotlin/event/message/local/LocalMessageBroadCaster.kt create mode 100644 event/src/main/kotlin/event/message/local/LocalMessageConfig.kt create mode 100644 event/src/main/kotlin/event/message/local/LocalSubscribeMessage.kt diff --git a/event/src/main/kotlin/event/message/local/LocalMessageBroadCaster.kt b/event/src/main/kotlin/event/message/local/LocalMessageBroadCaster.kt new file mode 100644 index 000000000..f85d9a6f4 --- /dev/null +++ b/event/src/main/kotlin/event/message/local/LocalMessageBroadCaster.kt @@ -0,0 +1,50 @@ +package event.message.local + +import com.fasterxml.jackson.databind.ObjectMapper +import event.message.Message +import event.message.MessagePayload +import event.message.MessageReverseRelay +import io.github.oshai.kotlinlogging.KotlinLogging +import org.springframework.context.event.EventListener +import org.springframework.scheduling.annotation.Async +import java.lang.reflect.Method + +fun Method.isForLocalMessageReverseRelay(): Boolean = this.annotations.any { annotation -> annotation is LocalSubscribeMessage } + +fun Method.messageType(): Class<*> = this.parameterTypes[0] + +@Suppress("LABEL_NAME_CLASH") +open class LocalMessageBroadCaster( + private val objectMapper: ObjectMapper, + private val messageReverseRelays: List>, +) { + private val log = KotlinLogging.logger { } + + @Async + @EventListener + open fun onApplicationEvent(message: Message) { + log.info { "[${Thread.currentThread().name}] Receive message: $message" } + messageReverseRelays.forEach { relay -> + relay.javaClass.methods + .filter { it.name.equals("onApplicationEvent") && it.isForLocalMessageReverseRelay() } + .forEach { method -> + val messageType = method.messageType() + method.getAnnotation(LocalSubscribeMessage::class.java).topic.let { + if (!method.name.contains(it)) { + return@forEach + } + } + objectMapper.writeValueAsString(message.payload).let { payload -> + objectMapper.readValue(payload, MessagePayload::class.java)?.let { messagePayload -> + objectMapper.convertValue(messagePayload, messageType).let { relayMessage -> + log.info { + "[${Thread.currentThread().name}] Publish message to ${relay.javaClass.simpleName}: $relayMessage" + } + method.invoke(relay, relayMessage) + } + } + } + } + } + } +} \ No newline at end of file diff --git a/event/src/main/kotlin/event/message/local/LocalMessageConfig.kt b/event/src/main/kotlin/event/message/local/LocalMessageConfig.kt new file mode 100644 index 000000000..faa85e693 --- /dev/null +++ b/event/src/main/kotlin/event/message/local/LocalMessageConfig.kt @@ -0,0 +1,48 @@ +package event.message.local + +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.module.kotlin.registerKotlinModule +import event.message.MessageReverseRelay +import io.github.oshai.kotlinlogging.KotlinLogging +import org.springframework.beans.factory.config.BeanFactoryPostProcessor +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.scheduling.annotation.EnableAsync + +@EnableAsync +@Configuration +class LocalMessageConfig { + val log = KotlinLogging.logger { } + + @Bean + @ConditionalOnMissingBean(ObjectMapper::class) + fun objectMapper(): ObjectMapper = + ObjectMapper().apply { + registerKotlinModule() + } + + @Bean + fun localMessageBeanPostProcessor( + objectMapper: ObjectMapper, + messageReverseRelays: List>, + ): BeanFactoryPostProcessor = + BeanFactoryPostProcessor { beanFactory -> + beanFactory.beanDefinitionNames + .filter { + it.contains("Local", ignoreCase = true) && + it.contains( + "MessageReverseRelay", + ignoreCase = true, + ) + }.takeIf { it.isNotEmpty() } + ?.also { localReverseBeanNames -> + log.info { "LocalReverse Beans are found: ${localReverseBeanNames.joinToString(", ")}" } + beanFactory.registerSingleton( + "localMessageBroadCaster", + LocalMessageBroadCaster(objectMapper, messageReverseRelays), + ) + log.info { "Local message broadcaster is registered" } + } + } +} \ No newline at end of file diff --git a/event/src/main/kotlin/event/message/local/LocalSubscribeMessage.kt b/event/src/main/kotlin/event/message/local/LocalSubscribeMessage.kt new file mode 100644 index 000000000..bcd716815 --- /dev/null +++ b/event/src/main/kotlin/event/message/local/LocalSubscribeMessage.kt @@ -0,0 +1,14 @@ +package event.message.local + +/** + * Local subscribe message + * + * 로컬 메시지 구독 정보 + * + * @property topic 메시지 토픽 + */ +@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION) +@Retention(AnnotationRetention.RUNTIME) +annotation class LocalSubscribeMessage( + val topic: String = "", +) \ No newline at end of file From a047e4c69c3532bb513480a9cc572bca02b319a0 Mon Sep 17 00:00:00 2001 From: belljun3395 <195850@jnu.ac.kr> Date: Fri, 20 Dec 2024 23:16:35 +0900 Subject: [PATCH 05/11] =?UTF-8?q?feat:=20EventTestFixtures=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/event/fixtures/TestEvent.kt | 23 ++++++++ .../event/fixtures/TestLocalMessageRelay.kt | 11 ++++ .../fixtures/TestLocalMessageReverseRelay.kt | 27 ++++++++++ .../event/fixtures/TestLocalMessageSender.kt | 12 +++++ .../kotlin/event/fixtures/TestMessage.kt | 27 ++++++++++ .../event/fixtures/TestReverseMessage.kt | 27 ++++++++++ .../kotlin/event/fixtures/TestTimeOutEvent.kt | 53 +++++++++++++++++++ 7 files changed, 180 insertions(+) create mode 100644 event/src/testFixtures/kotlin/event/fixtures/TestEvent.kt create mode 100644 event/src/testFixtures/kotlin/event/fixtures/TestLocalMessageRelay.kt create mode 100644 event/src/testFixtures/kotlin/event/fixtures/TestLocalMessageReverseRelay.kt create mode 100644 event/src/testFixtures/kotlin/event/fixtures/TestLocalMessageSender.kt create mode 100644 event/src/testFixtures/kotlin/event/fixtures/TestMessage.kt create mode 100644 event/src/testFixtures/kotlin/event/fixtures/TestReverseMessage.kt create mode 100644 event/src/testFixtures/kotlin/event/fixtures/TestTimeOutEvent.kt diff --git a/event/src/testFixtures/kotlin/event/fixtures/TestEvent.kt b/event/src/testFixtures/kotlin/event/fixtures/TestEvent.kt new file mode 100644 index 000000000..72ff8cee4 --- /dev/null +++ b/event/src/testFixtures/kotlin/event/fixtures/TestEvent.kt @@ -0,0 +1,23 @@ +package event.fixtures + +import event.Event +import event.EventDetails +import event.EventUtils + +@EventDetails(outBox = true) +class TestEvent( + eventId: String = EventUtils.generateEventId(), + eventType: String, + eventTime: Long = System.currentTimeMillis(), +) : Event( + eventId, + eventType, + eventTime, + ) { + override fun getData(): Map = + mapOf( + "eventId" to eventId, + "eventType" to eventType, + "eventTime" to eventTime, + ) +} \ No newline at end of file diff --git a/event/src/testFixtures/kotlin/event/fixtures/TestLocalMessageRelay.kt b/event/src/testFixtures/kotlin/event/fixtures/TestLocalMessageRelay.kt new file mode 100644 index 000000000..579be3efb --- /dev/null +++ b/event/src/testFixtures/kotlin/event/fixtures/TestLocalMessageRelay.kt @@ -0,0 +1,11 @@ +package event.fixtures + +import event.message.MessageRelay + +class TestLocalMessageRelay( + private val messageSender: TestLocalMessageSender, +) : MessageRelay { + override fun publish(message: TestMessage) { + messageSender.send(message) + } +} \ No newline at end of file diff --git a/event/src/testFixtures/kotlin/event/fixtures/TestLocalMessageReverseRelay.kt b/event/src/testFixtures/kotlin/event/fixtures/TestLocalMessageReverseRelay.kt new file mode 100644 index 000000000..3509f829f --- /dev/null +++ b/event/src/testFixtures/kotlin/event/fixtures/TestLocalMessageReverseRelay.kt @@ -0,0 +1,27 @@ +package event.fixtures + +import event.message.MessageReverseRelay +import event.message.local.LocalSubscribeMessage +import io.github.oshai.kotlinlogging.KotlinLogging +import org.springframework.context.event.EventListener + +class TestLocalMessageReverseRelay : MessageReverseRelay { + val log = KotlinLogging.logger { } + + @EventListener + @LocalSubscribeMessage(topic = "test") + fun onApplicationEvent(message: TestReverseMessage) { + log.info { "Receive Message: $message" } + publish( + TestEvent( + eventId = message.payload?.eventId!!, + eventType = message.payload?.eventType!!, + eventTime = message.payload?.eventTime!!, + ), + ) + } + + override fun publish(event: TestEvent) { + log.info { "Publish Event: $event" } + } +} \ No newline at end of file diff --git a/event/src/testFixtures/kotlin/event/fixtures/TestLocalMessageSender.kt b/event/src/testFixtures/kotlin/event/fixtures/TestLocalMessageSender.kt new file mode 100644 index 000000000..b72500709 --- /dev/null +++ b/event/src/testFixtures/kotlin/event/fixtures/TestLocalMessageSender.kt @@ -0,0 +1,12 @@ +package event.fixtures + +import event.message.MessageSender +import org.springframework.context.ApplicationEventPublisher + +class TestLocalMessageSender( + private val applicationEventPublisher: ApplicationEventPublisher, +) : MessageSender { + override fun send(message: TestMessage) { + applicationEventPublisher.publishEvent(message) + } +} \ No newline at end of file diff --git a/event/src/testFixtures/kotlin/event/fixtures/TestMessage.kt b/event/src/testFixtures/kotlin/event/fixtures/TestMessage.kt new file mode 100644 index 000000000..58a269651 --- /dev/null +++ b/event/src/testFixtures/kotlin/event/fixtures/TestMessage.kt @@ -0,0 +1,27 @@ +package event.fixtures + +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import event.message.Message +import event.message.MessagePayload + +class TestMessage( + payload: MessagePayload?, +) : Message(payload) { + @JsonCreator + constructor( + @JsonProperty("eventId") eventId: String?, + @JsonProperty("eventType") eventType: String?, + @JsonProperty("eventTime") eventTime: Long?, + @JsonProperty("data") data: Map?, + ) : this( + MessagePayload( + eventId, + eventType, + eventTime, + data, + ), + ) + + override fun toString(): String = "TestMessage(payload=$payload)" +} \ No newline at end of file diff --git a/event/src/testFixtures/kotlin/event/fixtures/TestReverseMessage.kt b/event/src/testFixtures/kotlin/event/fixtures/TestReverseMessage.kt new file mode 100644 index 000000000..c95895e89 --- /dev/null +++ b/event/src/testFixtures/kotlin/event/fixtures/TestReverseMessage.kt @@ -0,0 +1,27 @@ +package event.fixtures + +import com.fasterxml.jackson.annotation.JsonCreator +import com.fasterxml.jackson.annotation.JsonProperty +import event.message.Message +import event.message.MessagePayload + +class TestReverseMessage( + payload: MessagePayload?, +) : Message(payload) { + @JsonCreator + constructor( + @JsonProperty("eventId") eventId: String?, + @JsonProperty("eventType") eventType: String?, + @JsonProperty("eventTime") eventTime: Long?, + @JsonProperty("data") data: Map?, + ) : this( + MessagePayload( + eventId, + eventType, + eventTime, + data, + ), + ) + + override fun toString(): String = "TestMessage(payload=$payload)" +} \ No newline at end of file diff --git a/event/src/testFixtures/kotlin/event/fixtures/TestTimeOutEvent.kt b/event/src/testFixtures/kotlin/event/fixtures/TestTimeOutEvent.kt new file mode 100644 index 000000000..b1483a0ee --- /dev/null +++ b/event/src/testFixtures/kotlin/event/fixtures/TestTimeOutEvent.kt @@ -0,0 +1,53 @@ +package event.fixtures + +import event.EventDetails +import event.EventUtils +import event.TimeExpiredEvent +import event.TimeOutEvent +import org.springframework.context.ApplicationEventPublisher + +@EventDetails(outBox = false) +class TestTimeOutEvent( + eventId: String = EventUtils.generateEventId(), + eventType: String, + eventTime: Long = System.currentTimeMillis(), + expiredTime: Long, + completed: Boolean = false, + eventPublisher: ApplicationEventPublisher, +) : TimeOutEvent( + eventId, + eventType, + eventTime, + expiredTime, + completed, + eventPublisher, + ) { + override fun timeExpiredEvent(): TimeExpiredEvent = TestTimeExpiredEvent(eventId, eventType, eventTime) + + override fun getData(): Map = + mapOf( + "eventId" to eventId, + "eventType" to eventType, + "eventTime" to eventTime, + "expiredTime" to expiredTime, + "completed" to completed, + ) +} + +@EventDetails(outBox = false) +class TestTimeExpiredEvent( + eventId: String, + eventType: String, + eventTime: Long, +) : TimeExpiredEvent( + eventId, + eventType, + eventTime, + ) { + override fun getData(): Map = + mapOf( + "eventId" to eventId, + "eventType" to eventType, + "eventTime" to eventTime, + ) +} \ No newline at end of file From 5ad64be6fc97cd83350c581201fd8f7416789c3a Mon Sep 17 00:00:00 2001 From: belljun3395 <195850@jnu.ac.kr> Date: Fri, 20 Dec 2024 23:17:02 +0900 Subject: [PATCH 06/11] =?UTF-8?q?feat:=20EventRule=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../testFixtures/kotlin/event/EventRule.kt | 222 ++++++++++++++++++ 1 file changed, 222 insertions(+) create mode 100644 event/src/testFixtures/kotlin/event/EventRule.kt diff --git a/event/src/testFixtures/kotlin/event/EventRule.kt b/event/src/testFixtures/kotlin/event/EventRule.kt new file mode 100644 index 000000000..0e222ffc4 --- /dev/null +++ b/event/src/testFixtures/kotlin/event/EventRule.kt @@ -0,0 +1,222 @@ +package event + +import com.tngtech.archunit.base.DescribedPredicate +import com.tngtech.archunit.core.domain.JavaClass +import com.tngtech.archunit.core.domain.JavaModifier +import com.tngtech.archunit.core.importer.ClassFileImporter +import com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes +import com.tngtech.archunit.lang.syntax.ArchRuleDefinition.fields +import com.tngtech.archunit.lang.syntax.ArchRuleDefinition.methods +import com.tngtech.archunit.lang.syntax.elements.* +import event.message.Message +import event.message.MessageRelay +import event.message.MessageReverseRelay +import event.message.MessageSender +import event.message.local.LocalSubscribeMessage +import org.springframework.context.event.EventListener + +fun ClassesThat.areEvent(): GivenClassesConjunction = this.areAssignableTo(Event::class.java) + +fun ClassesThat.areNotAbstractClasses(): GivenClassesConjunction = this.doNotHaveModifier(JavaModifier.ABSTRACT) + +/** + * Event rule + * + * 이벤트 모듈의 규칙을 정의 하고 검증 할 수 있도록 지원 한다. + * + * 해당 모듈을 사용하는 프로젝트에서 EventRule을 상속 받아서 이벤트 관련 규칙을 검증 한다. + * + * `검증 예시`: + * ```kotlin + * class ApiEventRule : EventRule() { + * companion object { + * var classes: JavaClasses? = null + * + * @JvmStatic + * @BeforeAll + * fun setUp() { + * classes = ClassFileImporter().importPackages("com.few", "event") + * for (clazz in classes!!) { + * println(clazz.name) + * } + * } + * } + * + * @Test + * fun `TimeOutEvent 클래스와 TimeExpiredEvent는 @EventDetails의 outBox 속성이 false여야 한다`() { + * // 패키지를 지정하여 검증 + * assertDoesNotThrow { + * `규칙 - TimeOutEvent 클래스와 TimeExpiredEvent는 @EventDetails의 outBox 속성이 false여야 한다`("com.few", "event") + * } + * } + * + * @Test + * fun `MessageRelay는 MessageSender를 가지고 있어야 한다`() { + * // check 메서드를 사용하여 검증 + * `규칙 - MessageRelay는 MessageSender를 가지고 있어야 한다`().check(classes) + * } + * } + * + */ +@Suppress("ktlint:standard:function-naming", "ktlint:standard:max-line-length") +abstract class EventRule { + /** + * 이벤트 클래스는 @event details 어노테이션이 붙어있어야 한다 + */ + fun `규칙 - 이벤트 클래스는 @EventDetails 어노테이션이 붙어있어야 한다`(): ClassesShouldConjunction = + classes() + .that() + .areEvent() + .and() + .areNotAbstractClasses() + .should() + .beAnnotatedWith(EventDetails::class.java) + + /** + * time out event 클래스를 상속하면 time expired event도 상속해야 한다 + * + * @param packages 패키지 목록 + */ + fun `규칙 - TimeOutEvent 클래스를 상속하면 TimeExpiredEvent도 상속해야 한다`(vararg packages: String) { + val timeOutEvents = + ClassFileImporter() + .withImportOption { clazz -> + clazz.contains("TimeOutEvent") + }.importPackages(*packages) + + val timeExpiredEvents = + ClassFileImporter() + .withImportOption { clazz -> + clazz.contains("TimeExpiredEvent") + }.importPackages(*packages) + + if (timeOutEvents.size != timeExpiredEvents.size) { + throw AssertionError("TimeOutEvent 클래스의 수와 TimeExpiredEvent 클래스의 수가 다릅니다.") + } + } + + /** + * TimeOutEvent 클래스와 TimeExpiredEvent는 @EventDetails의 outBox 속성이 false여야 한다 + * + * @param packages 패키지 목록 + */ + fun `규칙 - TimeOutEvent 클래스와 TimeExpiredEvent는 @EventDetails의 outBox 속성이 false여야 한다`(vararg packages: String) { + val timeOutEvents = + ClassFileImporter() + .withImportOption { clazz -> + clazz.contains("TimeOutEvent") + }.importPackages(*packages) + + val timeExpiredEvents = + ClassFileImporter() + .withImportOption { clazz -> + clazz.contains("TimeExpiredEvent") + }.importPackages(*packages) + + (timeOutEvents + timeExpiredEvents) + .filterNot { + it.name.contains("$") + }.filterNot { + it.name.split(".").last() == "TimeOutEvent" || it.name.split(".").last() == ("TimeExpiredEvent") + }.forEach { event -> + if (!event.isAnnotatedWith(EventDetails::class.java)) { + throw AssertionError("EventDetails 어노테이션이 없습니다.") + } + + if (event.getAnnotationOfType(EventDetails::class.java).outBox) { + throw AssertionError("outBox 속성이 true입니다.") + } + } + } + + /** + * MessageRelay는 MessageSender를 가지고 있어야 한다 + */ + fun `규칙 - MessageRelay는 MessageSender를 가지고 있어야 한다`(): FieldsShouldConjunction { + val isSubclassOfMessageSender = + object : DescribedPredicate("is a subclass of MessageSender") { + override fun test(t: JavaClass?): Boolean = t?.isAssignableTo(MessageSender::class.java) ?: false + } + + return fields() + .that() + .areDeclaredInClassesThat() + .areAssignableTo(MessageRelay::class.java) + .should() + .haveRawType(isSubclassOfMessageSender) + } + + /** + * local message reverse relay는 on application event를 가지고 있어야 한다 + * + * @param packages 패키지 목록 + */ + fun `규칙 - Local MessageReverseRelay는 onApplicationEvent를 가지고 있어야 한다`(vararg packages: String): Unit = + ClassFileImporter() + .withImportOption { clazz -> + clazz.contains("Local") && clazz.contains("MessageReverseRelay") && !clazz.contains("$") + }.importPackages(*packages) + .groupBy { it } + .mapValues { (_, clazz) -> + clazz.first().methods.map { method -> method.name } + }.forEach { + if (!it.value.contains("onApplicationEvent")) { + throw AssertionError("onApplicationEvent 메서드가 없습니다.") + } + } + + /** + * message reverse relay는 @event listener이 붙어 있는 on application event를 가지고 있어야 한다 + */ + fun `규칙 - MessageReverseRelay는 @EventListener이 붙어 있는 onApplicationEvent를 가지고 있어야 한다`(): MethodsShouldConjunction = + methods() + .that() + .haveName("onApplicationEvent") + .and() + .areDeclaredInClassesThat() + .areAssignableTo(MessageReverseRelay::class.java) + .should() + .beAnnotatedWith(EventListener::class.java) + + /** + * message reverse relay는 @event listener이 붙어 있는 on application event는 message 클래스를 첫 번째 파라미터로 가지고 있어야 한다 + */ + fun `규칙 - MessageReverseRelay는 @EventListener이 붙어 있는 onApplicationEvent는 Message 클래스를 첫 번째 파라미터로 가지고 있어야 한다`(): MethodsShouldConjunction { + val isSubclassOfMessage = + object : DescribedPredicate>("is a subclass of Message") { + override fun test(t: List?): Boolean = t?.first()?.isAssignableTo(Message::class.java) ?: false + } + return methods() + .that() + .haveName("onApplicationEvent") + .and() + .areDeclaredInClassesThat() + .areAssignableTo(MessageReverseRelay::class.java) + .should() + .beAnnotatedWith(EventListener::class.java) + .andShould() + .haveRawParameterTypes(isSubclassOfMessage) + } + + /** + * local message reverse relay의 on application event 파라미터는 @local subscribe message 어노테이션에서 설정한 토픽의 이름을 포함해야 한다 + * + * @param packages 패키지 목록 + */ + fun `규칙 - Local MessageReverseRelay의 onApplicationEvent 파라미터는 @LocalSubscribeMessage 어노테이션에서 설정한 토픽의 이름을 포함해야 한다`( + vararg packages: String, + ): Unit = + ClassFileImporter() + .withImportOption { clazz -> + clazz.contains("Local") && clazz.contains("MessageReverseRelay") && !clazz.contains("$") + }.importPackages(*packages) + .filter { it.methods.any { method -> method.name == "onApplicationEvent" } } + .forEach { + val method = it.methods.first { method -> method.name == "onApplicationEvent" } + val topic = method.getAnnotationOfType(LocalSubscribeMessage::class.java).topic + val message = method.parameterTypes[0] + if (!message.name.contains(topic, ignoreCase = true)) { + throw AssertionError("onApplicationEvent 메서드의 이름에 토픽이 포함되어 있지 않습니다.") + } + } +} \ No newline at end of file From e03b1bca69d61ba979a636a298d25411e914e41d Mon Sep 17 00:00:00 2001 From: belljun3395 <195850@jnu.ac.kr> Date: Fri, 20 Dec 2024 23:17:24 +0900 Subject: [PATCH 07/11] =?UTF-8?q?test:=20MessageTest=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../test/kotlin/event/message/MessageTest.kt | 123 ++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 event/src/test/kotlin/event/message/MessageTest.kt diff --git a/event/src/test/kotlin/event/message/MessageTest.kt b/event/src/test/kotlin/event/message/MessageTest.kt new file mode 100644 index 000000000..c81edda4c --- /dev/null +++ b/event/src/test/kotlin/event/message/MessageTest.kt @@ -0,0 +1,123 @@ +package event.message + +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.module.kotlin.registerKotlinModule +import event.EventUtils +import event.fixtures.TestMessage +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test + +class MessageTest { + companion object { + val objectMapper = + ObjectMapper().apply { + registerKotlinModule() + } + } + + @Test + @DisplayName("Message 객체를 JSON으로 변환할 수 있다.") + fun to_json() { + // given + val message = + TestMessage( + MessagePayload( + eventId = EventUtils.generateEventId(), + eventType = "Test", + eventTime = System.currentTimeMillis(), + data = + mapOf( + "test" to "test", + ), + ), + ) + + // when + val json = objectMapper.writeValueAsString(message) + + // Then + val jsonTree = objectMapper.readTree(json) + val compareTree = + objectMapper.readTree( + """ + { + "payload": { + "eventId": "${message.payload?.eventId}", + "eventType": "${message.payload?.eventType}", + "eventTime": ${message.payload?.eventTime}, + "data": ${objectMapper.writeValueAsString(message.payload?.data)} + } + } + """.trimIndent(), + ) + assertTrue(jsonTree.equals(compareTree)) + } + + @Test + @DisplayName("JSON을 ObjectMapper의 readValue 메서드로 Message 객체로 변환할 수 있다.") + fun json_to_message_by_readValue() { + // given + val eventId = EventUtils.generateEventId() + val eventTime = System.currentTimeMillis() + val json = + """ + { + "payload": { + "eventId": "$eventId", + "eventType": "Test", + "eventTime": $eventTime, + "data": { + "test": "test" + } + } + } + """.trimIndent() + + // when + val message = objectMapper.readValue(json, TestMessage::class.java) + val compare = + TestMessage( + MessagePayload( + eventId = eventId, + eventType = "Test", + eventTime = eventTime, + data = mapOf("test" to "test"), + ), + ) + + // Then + assertEquals(compare, message) + } + + @Test + @DisplayName("MessagePayload 객체가 있으면 ObjectMapper의 convertValue 메서드로 Message 객체로 변환할 수 있다.") + fun message_payload_to_message_by_convertValue() { + // given + val eventId = EventUtils.generateEventId() + val eventTime = System.currentTimeMillis() + val messagePayload = + MessagePayload( + eventId = eventId, + eventType = "Test", + eventTime = eventTime, + data = mapOf("test" to "test"), + ) + + // when + val message = objectMapper.convertValue(messagePayload, TestMessage::class.java) + val compare = + TestMessage( + MessagePayload( + eventId = eventId, + eventType = "Test", + eventTime = eventTime, + data = mapOf("test" to "test"), + ), + ) + + // Then + assertEquals(compare, message) + } +} \ No newline at end of file From 6a12bbdb0af98eb03d57701ef7cba6a83d75fcb9 Mon Sep 17 00:00:00 2001 From: belljun3395 <195850@jnu.ac.kr> Date: Fri, 20 Dec 2024 23:17:40 +0900 Subject: [PATCH 08/11] =?UTF-8?q?test:=20LocalMessageBroadCasterTest=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../local/LocalMessageBroadCasterTest.kt | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 event/src/test/kotlin/event/message/local/LocalMessageBroadCasterTest.kt diff --git a/event/src/test/kotlin/event/message/local/LocalMessageBroadCasterTest.kt b/event/src/test/kotlin/event/message/local/LocalMessageBroadCasterTest.kt new file mode 100644 index 000000000..fa1b4ae5c --- /dev/null +++ b/event/src/test/kotlin/event/message/local/LocalMessageBroadCasterTest.kt @@ -0,0 +1,77 @@ +package event.message.local + +import event.EventUtils +import event.fixtures.TestLocalMessageReverseRelay +import event.fixtures.TestMessage +import event.message.MessagePayload +import io.github.oshai.kotlinlogging.KotlinLogging +import org.junit.jupiter.api.Assertions.assertNotNull +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Test +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.context.TestConfiguration +import org.springframework.context.ApplicationContext +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Profile +import org.springframework.test.context.junit.jupiter.SpringJUnitConfig +import java.io.ByteArrayOutputStream +import java.io.PrintStream + +@Profile("test, local") +@SpringJUnitConfig(LocalMessageBroadCasterTest.LocalMessageBroadCasterTestConfig::class, LocalMessageConfig::class) +class LocalMessageBroadCasterTest { + private val log = KotlinLogging.logger { } + + @TestConfiguration + class LocalMessageBroadCasterTestConfig { + @Bean + fun testLocalMessageReverseRelay(): TestLocalMessageReverseRelay = TestLocalMessageReverseRelay() + } + + @Autowired + lateinit var context: ApplicationContext + + @Test + fun is_registered_localMessageBroadCaster_bean() { + // given & when + val bean = context.getBean(LocalMessageBroadCaster::class.java) + + // then + assertNotNull(bean) + } + + @Test + fun localMessageBroadCaster_broadcast_message() { + val originalOut = System.out + var output: String? = null + try { + val outputStream = ByteArrayOutputStream() + System.setOut(PrintStream(outputStream)) + + // given + val message = + TestMessage( + MessagePayload( + eventId = EventUtils.generateEventId(), + eventType = "Test", + eventTime = System.currentTimeMillis(), + data = + mapOf( + "test" to "test", + ), + ), + ) + val broadcaster = context.getBean(LocalMessageBroadCaster::class.java) + + // when + broadcaster.onApplicationEvent(message) + + // then + output = outputStream.toString() + assertTrue(output.contains(message.toString())) + } finally { + System.setOut(originalOut) + log.info { output } + } + } +} \ No newline at end of file From c2477b76d06ff664c8758e6eed602e9d98f0754f Mon Sep 17 00:00:00 2001 From: belljun3395 <195850@jnu.ac.kr> Date: Sat, 21 Dec 2024 10:02:02 +0900 Subject: [PATCH 09/11] =?UTF-8?q?fix:=20message=EA=B0=80=20=EC=95=84?= =?UTF-8?q?=EB=8B=8C=20method=EB=A1=9C=20=EA=B2=80=EC=A6=9D=ED=95=98?= =?UTF-8?q?=EA=B3=A0=20=EC=9E=88=EB=8A=94=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/kotlin/event/message/local/LocalMessageBroadCaster.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/event/src/main/kotlin/event/message/local/LocalMessageBroadCaster.kt b/event/src/main/kotlin/event/message/local/LocalMessageBroadCaster.kt index f85d9a6f4..8e461de12 100644 --- a/event/src/main/kotlin/event/message/local/LocalMessageBroadCaster.kt +++ b/event/src/main/kotlin/event/message/local/LocalMessageBroadCaster.kt @@ -30,7 +30,7 @@ open class LocalMessageBroadCaster( .forEach { method -> val messageType = method.messageType() method.getAnnotation(LocalSubscribeMessage::class.java).topic.let { - if (!method.name.contains(it)) { + if (!message.javaClass.name.contains(it, ignoreCase = true)) { return@forEach } } From ff4f011f9e9ef45ef4c2187a3f402d0072f49ada Mon Sep 17 00:00:00 2001 From: belljun3395 <195850@jnu.ac.kr> Date: Sat, 21 Dec 2024 10:02:57 +0900 Subject: [PATCH 10/11] =?UTF-8?q?fix:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=98=EA=B2=8C=20json=EC=9C=BC=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=ED=99=94=ED=95=98=EB=8A=94=20=EA=B3=BC=EC=A0=95=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../event/message/local/LocalMessageBroadCaster.kt | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/event/src/main/kotlin/event/message/local/LocalMessageBroadCaster.kt b/event/src/main/kotlin/event/message/local/LocalMessageBroadCaster.kt index 8e461de12..e3d75bf9e 100644 --- a/event/src/main/kotlin/event/message/local/LocalMessageBroadCaster.kt +++ b/event/src/main/kotlin/event/message/local/LocalMessageBroadCaster.kt @@ -2,7 +2,6 @@ package event.message.local import com.fasterxml.jackson.databind.ObjectMapper import event.message.Message -import event.message.MessagePayload import event.message.MessageReverseRelay import io.github.oshai.kotlinlogging.KotlinLogging import org.springframework.context.event.EventListener @@ -34,15 +33,9 @@ open class LocalMessageBroadCaster( return@forEach } } - objectMapper.writeValueAsString(message.payload).let { payload -> - objectMapper.readValue(payload, MessagePayload::class.java)?.let { messagePayload -> - objectMapper.convertValue(messagePayload, messageType).let { relayMessage -> - log.info { - "[${Thread.currentThread().name}] Publish message to ${relay.javaClass.simpleName}: $relayMessage" - } - method.invoke(relay, relayMessage) - } - } + objectMapper.convertValue(message.payload, messageType).let { relayMessage -> + log.info { "[${Thread.currentThread().name}] Publish message to ${relay.javaClass.simpleName}: $relayMessage" } + method.invoke(relay, relayMessage) } } } From f8ddcf6b05cb1c662280dac50ad73c860e31790c Mon Sep 17 00:00:00 2001 From: belljun3395 <195850@jnu.ac.kr> Date: Sat, 21 Dec 2024 10:07:18 +0900 Subject: [PATCH 11/11] =?UTF-8?q?refactor:=20LocalMessageBroadCaster=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D=EC=97=90=EC=84=9C=20=ED=95=9C=EC=A4=84?= =?UTF-8?q?=EC=97=90=20message=EC=99=80=20messageReverseRelay=EA=B0=80=20?= =?UTF-8?q?=EB=82=98=EC=98=A4=EB=8A=94=EC=A7=80=20=ED=99=95=EC=9D=B8?= =?UTF-8?q?=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../event/message/local/LocalMessageBroadCasterTest.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/event/src/test/kotlin/event/message/local/LocalMessageBroadCasterTest.kt b/event/src/test/kotlin/event/message/local/LocalMessageBroadCasterTest.kt index fa1b4ae5c..84ee7caec 100644 --- a/event/src/test/kotlin/event/message/local/LocalMessageBroadCasterTest.kt +++ b/event/src/test/kotlin/event/message/local/LocalMessageBroadCasterTest.kt @@ -68,7 +68,11 @@ class LocalMessageBroadCasterTest { // then output = outputStream.toString() - assertTrue(output.contains(message.toString())) + val isMatch = + output.split("\n").stream().anyMatch { + it.contains(message.toString(), ignoreCase = true) && it.contains("MessageReverseRelay", ignoreCase = true) + } + assertTrue(isMatch) } finally { System.setOut(originalOut) log.info { output }