From bd917fb24bda1438fefa39b71ec95cf5459373b6 Mon Sep 17 00:00:00 2001 From: Anurag Patil Date: Tue, 25 Oct 2022 16:44:20 +0530 Subject: [PATCH] feat: initial event dispatcher abstraction, implemented in chapter downloader --- .../kotlin/suwayomi/tachidesk/event/Event.kt | 10 +++ .../tachidesk/event/EventDispatcher.kt | 41 ++++++++++++ .../suwayomi/tachidesk/event/EventEntity.kt | 3 + .../tachidesk/event/enums/EventType.kt | 8 +++ .../manga/controller/DownloadController.kt | 6 +- .../impl/download/DownloadEventDispatcher.kt | 30 +++++++++ .../manga/impl/download/DownloadManager.kt | 64 ++++++------------- .../impl/download/model/DownloadChapter.kt | 3 +- .../impl/download/model/DownloadStatus.kt | 4 +- 9 files changed, 120 insertions(+), 49 deletions(-) create mode 100644 server/src/main/kotlin/suwayomi/tachidesk/event/Event.kt create mode 100644 server/src/main/kotlin/suwayomi/tachidesk/event/EventDispatcher.kt create mode 100644 server/src/main/kotlin/suwayomi/tachidesk/event/EventEntity.kt create mode 100644 server/src/main/kotlin/suwayomi/tachidesk/event/enums/EventType.kt create mode 100644 server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/DownloadEventDispatcher.kt diff --git a/server/src/main/kotlin/suwayomi/tachidesk/event/Event.kt b/server/src/main/kotlin/suwayomi/tachidesk/event/Event.kt new file mode 100644 index 000000000..bd2e9ae72 --- /dev/null +++ b/server/src/main/kotlin/suwayomi/tachidesk/event/Event.kt @@ -0,0 +1,10 @@ +package suwayomi.tachidesk.event + +import suwayomi.tachidesk.event.enums.EventType +import java.util.UUID + +data class Event( + val id: UUID? = UUID.randomUUID(), + val type: EventType, + val entity: T +) diff --git a/server/src/main/kotlin/suwayomi/tachidesk/event/EventDispatcher.kt b/server/src/main/kotlin/suwayomi/tachidesk/event/EventDispatcher.kt new file mode 100644 index 000000000..480e3a6f3 --- /dev/null +++ b/server/src/main/kotlin/suwayomi/tachidesk/event/EventDispatcher.kt @@ -0,0 +1,41 @@ +package suwayomi.tachidesk.event + +import io.javalin.websocket.WsContext +import io.javalin.websocket.WsMessageContext +import java.util.concurrent.ConcurrentHashMap +import java.util.concurrent.CopyOnWriteArrayList + +abstract class EventDispatcher { + private val clients = ConcurrentHashMap() + private val eventQueue = CopyOnWriteArrayList>() + + fun addClient(ctx: WsContext) { + clients[ctx.sessionId] = ctx + } + + fun removeClient(ctx: WsContext) { + clients.remove(key = ctx.sessionId) + } + + abstract fun notifyClient(ctx: WsContext, event: Event) + + abstract fun handleRequest(ctx: WsMessageContext) + + fun notifyAllClients(event: Event) { + clients.forEach { + notifyClient(ctx = it.value, event = event) + } + } + + fun enqueue(event: Event) { + eventQueue += event + } + + // to be consumed in the client based on + // the event type in a notifications screen + fun queue() = eventQueue + + fun dequeue(event: Event) { + eventQueue.removeIf { it.id == event.id } + } +} diff --git a/server/src/main/kotlin/suwayomi/tachidesk/event/EventEntity.kt b/server/src/main/kotlin/suwayomi/tachidesk/event/EventEntity.kt new file mode 100644 index 000000000..e9aae5a6a --- /dev/null +++ b/server/src/main/kotlin/suwayomi/tachidesk/event/EventEntity.kt @@ -0,0 +1,3 @@ +package suwayomi.tachidesk.event + +abstract class EventEntity diff --git a/server/src/main/kotlin/suwayomi/tachidesk/event/enums/EventType.kt b/server/src/main/kotlin/suwayomi/tachidesk/event/enums/EventType.kt new file mode 100644 index 000000000..af9f156bc --- /dev/null +++ b/server/src/main/kotlin/suwayomi/tachidesk/event/enums/EventType.kt @@ -0,0 +1,8 @@ +package suwayomi.tachidesk.event.enums + +enum class EventType { + SYSTEM, + INFORMATIVE, + STATIC, + DYNAMIC +} diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/controller/DownloadController.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/controller/DownloadController.kt index 065afe76f..7a324c230 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/controller/DownloadController.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/controller/DownloadController.kt @@ -19,14 +19,14 @@ object DownloadController { /** Download queue stats */ fun downloadsWS(ws: WsConfig) { ws.onConnect { ctx -> - DownloadManager.addClient(ctx) + DownloadManager.eventDispatcher.addClient(ctx) DownloadManager.notifyClient(ctx) } ws.onMessage { ctx -> - DownloadManager.handleRequest(ctx) + DownloadManager.eventDispatcher.handleRequest(ctx) } ws.onClose { ctx -> - DownloadManager.removeClient(ctx) + DownloadManager.eventDispatcher.removeClient(ctx) } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/DownloadEventDispatcher.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/DownloadEventDispatcher.kt new file mode 100644 index 000000000..b2e7d47bb --- /dev/null +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/DownloadEventDispatcher.kt @@ -0,0 +1,30 @@ +package suwayomi.tachidesk.manga.impl.download + +import io.javalin.websocket.WsContext +import io.javalin.websocket.WsMessageContext +import suwayomi.tachidesk.event.Event +import suwayomi.tachidesk.event.EventDispatcher +import suwayomi.tachidesk.manga.impl.download.model.DownloadStatus + +class DownloadEventDispatcher : EventDispatcher() { + override fun notifyClient(ctx: WsContext, event: Event) { + ctx.send( + event.entity + ) + } + + override fun handleRequest(ctx: WsMessageContext) { + when (ctx.message()) { + "STATUS" -> DownloadManager.notifyClient(ctx) + else -> ctx.send( + """ + |Invalid command. + |Supported commands are: + | - STATUS + | sends the current download status + | + """.trimMargin() + ) + } + } +} diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/DownloadManager.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/DownloadManager.kt index 987809181..e46f349cd 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/DownloadManager.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/DownloadManager.kt @@ -8,62 +8,30 @@ package suwayomi.tachidesk.manga.impl.download * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ import io.javalin.websocket.WsContext -import io.javalin.websocket.WsMessageContext import org.jetbrains.exposed.sql.and import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.transactions.transaction +import suwayomi.tachidesk.event.Event +import suwayomi.tachidesk.event.enums.EventType import suwayomi.tachidesk.manga.impl.Manga.getManga import suwayomi.tachidesk.manga.impl.download.model.DownloadChapter import suwayomi.tachidesk.manga.impl.download.model.DownloadState.Downloading import suwayomi.tachidesk.manga.impl.download.model.DownloadStatus import suwayomi.tachidesk.manga.model.table.ChapterTable import suwayomi.tachidesk.manga.model.table.toDataClass -import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.CopyOnWriteArrayList object DownloadManager { - private val clients = ConcurrentHashMap() + val eventDispatcher = DownloadEventDispatcher() private val downloadQueue = CopyOnWriteArrayList() private var downloader: Downloader? = null - fun addClient(ctx: WsContext) { - clients[ctx.sessionId] = ctx - } - - fun removeClient(ctx: WsContext) { - clients.remove(ctx.sessionId) - } - fun notifyClient(ctx: WsContext) { - ctx.send( - getStatus() - ) - } - - fun handleRequest(ctx: WsMessageContext) { - when (ctx.message()) { - "STATUS" -> notifyClient(ctx) - else -> ctx.send( - """ - |Invalid command. - |Supported commands are: - | - STATUS - | sends the current download status - | - """.trimMargin() - ) - } - } - - private fun notifyAllClients() { - val status = getStatus() - clients.forEach { - it.value.send(status) - } + eventDispatcher.notifyClient(ctx, getStatus()) } - private fun getStatus(): DownloadStatus { - return DownloadStatus( + private fun getStatus(): Event { + val status = DownloadStatus( if (downloader == null || downloadQueue.none { it.state == Downloading } ) { @@ -73,6 +41,10 @@ object DownloadManager { }, downloadQueue ) + return Event( + type = EventType.STATIC, + entity = status + ) } suspend fun enqueue(chapterIndex: Int, mangaId: Int) { @@ -92,12 +64,16 @@ object DownloadManager { ) start() } - notifyAllClients() + val status = getStatus() + eventDispatcher.enqueue(status) + eventDispatcher.notifyAllClients(status) } fun unqueue(chapterIndex: Int, mangaId: Int) { downloadQueue.removeIf { it.mangaId == mangaId && it.chapterIndex == chapterIndex } - notifyAllClients() + val status = getStatus() + eventDispatcher.dequeue(status) + eventDispatcher.notifyAllClients(status) } fun start() { @@ -107,11 +83,11 @@ object DownloadManager { } if (downloader == null) { - downloader = Downloader(downloadQueue) { notifyAllClients() } + downloader = Downloader(downloadQueue) { eventDispatcher.notifyAllClients(getStatus()) } downloader!!.start() } - notifyAllClients() + eventDispatcher.notifyAllClients(getStatus()) } fun stop() { @@ -121,13 +97,13 @@ object DownloadManager { } } downloader = null - notifyAllClients() + eventDispatcher.notifyAllClients(getStatus()) } fun clear() { stop() downloadQueue.clear() - notifyAllClients() + eventDispatcher.notifyAllClients(getStatus()) } } diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/model/DownloadChapter.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/model/DownloadChapter.kt index f101150b6..168cd8f9d 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/model/DownloadChapter.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/model/DownloadChapter.kt @@ -7,6 +7,7 @@ package suwayomi.tachidesk.manga.impl.download.model * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +import suwayomi.tachidesk.event.EventEntity import suwayomi.tachidesk.manga.impl.download.model.DownloadState.Queued import suwayomi.tachidesk.manga.model.dataclass.ChapterDataClass import suwayomi.tachidesk.manga.model.dataclass.MangaDataClass @@ -19,4 +20,4 @@ class DownloadChapter( var state: DownloadState = Queued, var progress: Float = 0f, var tries: Int = 0 -) +) : EventEntity() diff --git a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/model/DownloadStatus.kt b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/model/DownloadStatus.kt index c2f748afb..df834e24b 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/model/DownloadStatus.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/manga/impl/download/model/DownloadStatus.kt @@ -1,5 +1,7 @@ package suwayomi.tachidesk.manga.impl.download.model +import suwayomi.tachidesk.event.EventEntity + /* * Copyright (C) Contributors to the Suwayomi project * @@ -10,4 +12,4 @@ package suwayomi.tachidesk.manga.impl.download.model data class DownloadStatus( val status: String, val queue: List -) +) : EventEntity()