Skip to content

Commit f496aac

Browse files
nomisReve5l
andauthored
Refactor NewsService and SessionizeService to use CoroutineScope for job lifecycle management and adjust DI module accordingly. (#531)
Co-authored-by: Leonid Stashevsky <[email protected]>
1 parent 4c3220d commit f496aac

File tree

3 files changed

+53
-54
lines changed

3 files changed

+53
-54
lines changed

backend/src/main/kotlin/org/jetbrains/kotlinconf/backend/DiModule.kt

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,45 +4,46 @@ import io.ktor.client.*
44
import io.ktor.client.plugins.contentnegotiation.*
55
import io.ktor.serialization.kotlinx.json.*
66
import io.ktor.server.application.*
7-
import io.ktor.utils.io.core.*
7+
import kotlinx.coroutines.CoroutineScope
88
import kotlinx.serialization.json.Json
99
import org.jetbrains.kotlinconf.backend.di.configModule
1010
import org.jetbrains.kotlinconf.backend.di.repositoryModule
1111
import org.jetbrains.kotlinconf.backend.di.serviceModule
1212
import org.koin.dsl.module
13+
import org.koin.dsl.onClose
1314
import org.koin.ktor.ext.getKoin
1415
import org.koin.ktor.plugin.Koin
1516

1617
fun Application.diModule() {
17-
val client = HttpClient {
18-
install(ContentNegotiation) {
19-
json(Json {
20-
ignoreUnknownKeys = true
21-
22-
encodeDefaults = true
23-
isLenient = true
24-
allowSpecialFloatingPointValues = true
25-
allowStructuredMapKeys = true
26-
prettyPrint = false
27-
useArrayPolymorphism = false
28-
})
29-
}
30-
}
31-
3218
install(Koin) {
3319
modules(
3420
serviceModule,
3521
repositoryModule,
3622
configModule,
3723
module {
38-
single { client }
24+
single<CoroutineScope> { this@diModule }
25+
single { createClient() } onClose { it?.close() }
3926
single { environment.config }
4027
}
4128
)
4229
}
4330

44-
monitor.subscribe(ApplicationStopping) {
45-
val closeableServices = it.getKoin().getAll<Closeable>()
46-
closeableServices.forEach { it.close() }
31+
monitor.subscribe(ApplicationStopped) {
32+
it.getKoin().close()
33+
}
34+
}
35+
36+
private fun createClient() = HttpClient {
37+
install(ContentNegotiation) {
38+
json(Json {
39+
ignoreUnknownKeys = true
40+
41+
encodeDefaults = true
42+
isLenient = true
43+
allowSpecialFloatingPointValues = true
44+
allowStructuredMapKeys = true
45+
prettyPrint = false
46+
useArrayPolymorphism = false
47+
})
4748
}
48-
}
49+
}

backend/src/main/kotlin/org/jetbrains/kotlinconf/backend/services/NewsService.kt

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ import io.ktor.client.*
44
import io.ktor.client.call.*
55
import io.ktor.client.request.*
66
import io.ktor.utils.io.core.*
7+
import kotlinx.coroutines.CoroutineScope
78
import kotlinx.coroutines.Dispatchers
8-
import kotlinx.coroutines.GlobalScope
9+
import kotlinx.coroutines.delay
910
import kotlinx.coroutines.flow.MutableSharedFlow
1011
import kotlinx.coroutines.flow.first
1112
import kotlinx.coroutines.launch
@@ -15,7 +16,11 @@ import org.jetbrains.kotlinconf.backend.model.GitHubItem
1516
import org.jetbrains.kotlinconf.backend.utils.ConferenceConfig
1617
import org.slf4j.LoggerFactory
1718

18-
class NewsService(private val client: HttpClient, config: ConferenceConfig): Closeable {
19+
class NewsService(
20+
private val client: HttpClient,
21+
scope: CoroutineScope,
22+
config: ConferenceConfig
23+
) {
1924
private val log = LoggerFactory.getLogger("NewsService")
2025
private val repo: String = config.newsRepo
2126
private val branch: String = config.newsBranch
@@ -24,13 +29,15 @@ class NewsService(private val client: HttpClient, config: ConferenceConfig): Clo
2429

2530
private val news = MutableSharedFlow<List<NewsItem>>(replay = 1)
2631

27-
val syncJob = GlobalScope.launch(Dispatchers.IO) {
28-
while (true) {
29-
runCatching { updateNews() }.onFailure {
30-
log.error("Failed to update news", it)
31-
}
32+
init {
33+
scope.launch(Dispatchers.IO) {
34+
while (true) {
35+
runCatching { updateNews() }.onFailure {
36+
log.error("Failed to update news", it)
37+
}
3238

33-
kotlinx.coroutines.delay(updateInterval * 1000)
39+
delay(updateInterval * 1000)
40+
}
3441
}
3542
}
3643

@@ -127,9 +134,4 @@ class NewsService(private val client: HttpClient, config: ConferenceConfig): Clo
127134
content = bodyContent
128135
)
129136
}
130-
131-
132-
override fun close() {
133-
syncJob.cancel()
134-
}
135137
}

backend/src/main/kotlin/org/jetbrains/kotlinconf/backend/services/SessionizeService.kt

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,7 @@ package org.jetbrains.kotlinconf.backend.services
55
import io.ktor.client.HttpClient
66
import io.ktor.client.call.body
77
import io.ktor.client.request.get
8-
import io.ktor.utils.io.core.Closeable
9-
import kotlinx.coroutines.DelicateCoroutinesApi
10-
import kotlinx.coroutines.GlobalScope
8+
import kotlinx.coroutines.CoroutineScope
119
import kotlinx.coroutines.delay
1210
import kotlinx.coroutines.flow.MutableSharedFlow
1311
import kotlinx.coroutines.flow.first
@@ -26,27 +24,29 @@ import kotlin.time.ExperimentalTime
2624

2725
class SessionizeService(
2826
private val client: HttpClient,
27+
scope: CoroutineScope,
2928
config: ConferenceConfig
30-
): Closeable {
29+
) {
3130
private val conference = MutableSharedFlow<Conference>(replay = 1)
3231

3332
private val sessionizeUrl: String = config.sessionizeUrl
3433
private val sessionizeInterval: Long = config.sessionizeInterval
3534
private val log = LoggerFactory.getLogger("SessionizeService")
3635

37-
@OptIn(DelicateCoroutinesApi::class)
38-
private val syncJob = GlobalScope.launch {
39-
log.info("Synchronizing each $sessionizeInterval minutes with $sessionizeUrl")
40-
while (true) {
41-
log.trace("Synchronizing to Sessionize…")
42-
runCatching {
43-
synchronizeWithSessionize(sessionizeUrl)
44-
}.onFailure { cause ->
45-
log.error("Failed to synchronize to Sessionize: ${cause.message}", cause)
36+
init {
37+
scope.launch {
38+
log.info("Synchronizing each $sessionizeInterval minutes with $sessionizeUrl")
39+
while (true) {
40+
log.trace("Synchronizing to Sessionize…")
41+
runCatching {
42+
synchronizeWithSessionize(sessionizeUrl)
43+
}.onFailure { cause ->
44+
log.error("Failed to synchronize to Sessionize: ${cause.message}", cause)
45+
}
46+
47+
log.trace("Finished loading data from Sessionize.")
48+
delay(TimeUnit.MINUTES.toMillis(sessionizeInterval))
4649
}
47-
48-
log.trace("Finished loading data from Sessionize.")
49-
delay(TimeUnit.MINUTES.toMillis(sessionizeInterval))
5050
}
5151
}
5252

@@ -134,8 +134,4 @@ class SessionizeService(
134134

135135
return nonWorkshop + workshops
136136
}
137-
138-
override fun close() {
139-
syncJob.cancel()
140-
}
141137
}

0 commit comments

Comments
 (0)