Skip to content

Commit fdc1a1f

Browse files
authored
Add Quartz Scheduler (#7)
* init scheduler * clean repo before pull * Fix collision
1 parent cd8ab97 commit fdc1a1f

File tree

15 files changed

+323
-11
lines changed

15 files changed

+323
-11
lines changed

.idea/misc.xml

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

gradle/libs.versions.toml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,15 @@ kotlinLogging = "5.1.0"
2424
junit = "5.9.2"
2525
jsonToolsJsonPatch = "1.13"
2626
mustache = "0.9.10"
27+
quartz = "2.5.0"
28+
exposed-version = "0.58.0"
29+
c3p0 = "0.10.1"
2730

2831
[libraries]
2932
ktor-server-core = { module = "io.ktor:ktor-server-core-jvm", version.ref = "ktor" }
3033
ktor-server-netty = { module = "io.ktor:ktor-server-netty-jvm", version.ref = "ktor" }
3134
ktor-server-webjars = { module = "io.ktor:ktor-server-webjars-jvm", version.ref = "ktor" }
35+
ktor-server-cors = { module = "io.ktor:ktor-server-cors", version.ref = "ktor" }
3236
jquery = { module = "org.webjars:jquery", version.ref = "jquery" }
3337
ktor-swagger-ui = { module = "io.github.smiley4:ktor-swagger-ui", version.ref = "swagger-ui" }
3438
ktor-server-auth = { module = "io.ktor:ktor-server-auth-jvm", version.ref = "ktor" }
@@ -79,6 +83,14 @@ gson = { group = "com.google.code.gson", name = "gson", version.ref = "gson" }
7983
kaml = { group = "com.charleskorn.kaml", name = "kaml", version.ref = "kaml" }
8084
hamcrest-core = { group = "org.hamcrest", name = "hamcrest-core", version.ref = "hamcrest" }
8185

86+
quartz = { group = "org.quartz-scheduler", name = "quartz", version.ref = "quartz" }
87+
c3p0 = { group = "com.mchange", name = "c3p0", version.ref = "c3p0" }
88+
89+
exposed-core = { module = "org.jetbrains.exposed:exposed-core", version.ref = "exposed-version" }
90+
exposed-jdbc = { module = "org.jetbrains.exposed:exposed-jdbc", version.ref = "exposed-version" }
91+
exposed-dao = { module = "org.jetbrains.exposed:exposed-dao", version.ref = "exposed-version" }
92+
exposed-java-time = { module = "org.jetbrains.exposed:exposed-java-time", version.ref = "exposed-version" }
93+
8294
# Testing dependencies
8395
junit-jupiter-api = { group = "org.junit.jupiter", name = "junit-jupiter-api", version.ref = "junit" }
8496
junit-jupiter-engine = { group = "org.junit.jupiter", name = "junit-jupiter-engine", version.ref = "junit" }

server/build.gradle.kts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,9 @@ dependencies {
3737

3838
implementation(libs.ktor.server.core)
3939
implementation(libs.ktor.server.netty)
40-
40+
implementation(libs.ktor.server.cors)
4141
implementation(libs.ktor.server.webjars)
42+
4243
implementation(libs.jquery)
4344
implementation(libs.ktor.swagger.ui)
4445
implementation(libs.ktor.server.auth)
@@ -52,7 +53,6 @@ dependencies {
5253
implementation(libs.koin.logger.slf4j)
5354
implementation(libs.ktor.serialization.json)
5455
implementation(libs.ktor.server.content.negotiation)
55-
implementation(libs.postgresql)
5656
implementation(libs.h2)
5757

5858
implementation(libs.logback.classic)
@@ -66,6 +66,15 @@ dependencies {
6666

6767
implementation(libs.spullara.mustache)
6868

69+
implementation(libs.quartz)
70+
implementation(libs.c3p0)
71+
72+
implementation(libs.exposed.core)
73+
implementation(libs.exposed.jdbc)
74+
implementation(libs.exposed.dao)
75+
implementation(libs.exposed.java.time)
76+
implementation(libs.postgresql)
77+
6978
testImplementation(libs.ktor.server.test.host)
7079
testImplementation(libs.kotlin.test.junit)
7180

server/src/main/kotlin/org/dtree/fhir/server/Application.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package org.dtree.fhir.server
33
import io.ktor.server.application.*
44
import org.dtree.fhir.server.plugins.configureFrameworks
55
import org.dtree.fhir.server.plugins.configureRouting
6+
import org.dtree.fhir.server.plugins.configureScheduler
67
import org.dtree.fhir.server.plugins.configureSecurity
78
import org.dtree.fhir.server.util.loadEnv
89

@@ -16,4 +17,6 @@ fun Application.module() {
1617
configureFrameworks(dotEnv)
1718
configureSecurity()
1819
configureRouting()
20+
21+
configureScheduler()
1922
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package org.dtree.fhir.server.data
2+
3+
import org.jetbrains.exposed.sql.Table
4+
import org.jetbrains.exposed.sql.javatime.datetime
5+
6+
object JobHistoryTable : Table("job_history") {
7+
val id = integer("id").autoIncrement()
8+
val jobName = varchar("job_name", 100)
9+
val jobGroup = varchar("job_group", 100)
10+
val startTime = datetime("start_time")
11+
val endTime = datetime("end_time").nullable()
12+
val status = varchar("status", 20)
13+
val message = text("message").nullable()
14+
val retryCount = integer("retry_count").default(0)
15+
16+
override val primaryKey = PrimaryKey(id)
17+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package org.dtree.fhir.server.data
2+
3+
import org.jetbrains.exposed.sql.*
4+
import org.jetbrains.exposed.sql.transactions.transaction
5+
import java.time.LocalDateTime
6+
7+
class JobHistoryRepository(database: Database) {
8+
9+
init {
10+
transaction(database) {
11+
SchemaUtils.create(JobHistoryTable)
12+
}
13+
}
14+
15+
fun logJobStart(jobName: String, jobGroup: String): Int = transaction {
16+
JobHistoryTable.insert {
17+
it[this.jobName] = jobName
18+
it[this.jobGroup] = jobGroup
19+
it[startTime] = LocalDateTime.now()
20+
it[status] = "RUNNING"
21+
} get JobHistoryTable.id
22+
}
23+
24+
fun logJobEnd(historyId: Int, status: String, message: String? = null) = transaction {
25+
JobHistoryTable.update({ JobHistoryTable.id eq historyId }) {
26+
it[endTime] = LocalDateTime.now()
27+
it[this.status] = status
28+
it[this.message] = message
29+
}
30+
}
31+
32+
fun updateRetryCount(historyId: Int, retryCount: Int) = transaction {
33+
JobHistoryTable.update({ JobHistoryTable.id eq historyId }) {
34+
it[this.retryCount] = retryCount
35+
}
36+
}
37+
38+
fun getJobHistory(jobName: String, jobGroup: String, limit: Int = 100) = transaction {
39+
JobHistoryTable.selectAll()
40+
.where { (JobHistoryTable.jobName eq jobName) and (JobHistoryTable.jobGroup eq jobGroup) }
41+
.orderBy(JobHistoryTable.startTime to SortOrder.DESC)
42+
.limit(limit)
43+
.map {
44+
mapOf(
45+
"id" to it[JobHistoryTable.id],
46+
"startTime" to it[JobHistoryTable.startTime],
47+
"endTime" to it[JobHistoryTable.endTime],
48+
"status" to it[JobHistoryTable.status],
49+
"message" to it[JobHistoryTable.message],
50+
"retryCount" to it[JobHistoryTable.retryCount]
51+
)
52+
}
53+
}
54+
}

server/src/main/kotlin/org/dtree/fhir/server/plugins/Frameworks.kt

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,15 @@ import io.ktor.serialization.gson.*
55
import io.ktor.server.application.*
66
import io.ktor.server.plugins.contentnegotiation.*
77
import org.dtree.fhir.core.di.FhirProvider
8+
import org.dtree.fhir.server.data.JobHistoryRepository
89
import org.dtree.fhir.server.plugins.injection.ModulesInjection
10+
import org.dtree.fhir.server.plugins.scheduler.JobFactory
11+
import org.dtree.fhir.server.plugins.scheduler.JobSchedulerManager
912
import org.dtree.fhir.server.services.injection.ServicesInjection
13+
import org.dtree.fhir.server.services.tracing.TracingService
1014
import org.dtree.fhir.server.util.LocalDateAdapter
1115
import org.dtree.fhir.server.util.LocalDateTimeTypeAdapter
16+
import org.jetbrains.exposed.sql.Database
1217
import org.koin.core.logger.Level
1318
import org.koin.dsl.module
1419
import org.koin.ktor.plugin.Koin
@@ -19,7 +24,7 @@ import java.time.LocalDateTime
1924
fun Application.configureFrameworks(dotEnv: Dotenv) {
2025
install(ContentNegotiation) {
2126
gson {
22-
registerTypeAdapter(LocalDate::class.java,LocalDateAdapter())
27+
registerTypeAdapter(LocalDate::class.java, LocalDateAdapter())
2328
registerTypeAdapter(LocalDateTime::class.java, LocalDateTimeTypeAdapter())
2429
}
2530
}
@@ -28,7 +33,20 @@ fun Application.configureFrameworks(dotEnv: Dotenv) {
2833
modules(module {
2934
single { dotEnv }
3035
single { FhirProvider() }
31-
}, ModulesInjection.koinBeans, ServicesInjection.koinBeans)
36+
single<Database>(createdAtStart = true) {
37+
Database.connect(
38+
url = dotEnv["DB_URL"] ?: "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1",
39+
user = dotEnv["DB_USER"] ?: "sa",
40+
driver = dotEnv["DB_DRIVER"] ?: "org.h2.Driver",
41+
password = dotEnv["DB_PASSWORD"] ?: "",
42+
)
43+
}
44+
}, ModulesInjection.koinBeans, ServicesInjection.koinBeans, module {
45+
single<JobHistoryRepository> {
46+
JobHistoryRepository(this.get<Database>())
47+
}
48+
single { JobSchedulerManager(dotEnv, JobFactory(this.get<TracingService>(), this.get())) }
49+
})
3250

3351
}
3452
}

server/src/main/kotlin/org/dtree/fhir/server/plugins/Routing.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import io.ktor.server.routing.get
1414
import org.dtree.fhir.server.plugins.appointment.appointmentModule
1515
import org.dtree.fhir.server.plugins.careplan.carePlanModule
1616
import org.dtree.fhir.server.plugins.patient.patientModule
17+
import org.dtree.fhir.server.plugins.scheduler.schedulerRoutes
1718
import org.dtree.fhir.server.plugins.stats.statsModule
1819
import org.dtree.fhir.server.plugins.tasks.tasksModule
1920
import org.dtree.fhir.server.plugins.tracing.tracingModule
@@ -51,6 +52,7 @@ fun Application.configureRouting() {
5152
carePlanModule()
5253
tasksModule()
5354
patientModule()
55+
schedulerRoutes()
5456

5557
get("/") {
5658
call.respondText("Hello, World!")
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package org.dtree.fhir.server.plugins
2+
3+
import io.ktor.server.application.*
4+
import org.dtree.fhir.server.plugins.scheduler.JobSchedulerManager
5+
import org.koin.ktor.ext.inject
6+
7+
fun Application.configureScheduler() {
8+
val jobSchedulerManager by inject<JobSchedulerManager>()
9+
10+
jobSchedulerManager.startScheduler()
11+
}

server/src/main/kotlin/org/dtree/fhir/server/plugins/injection/ModulesInjection.kt

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,8 @@ import org.dtree.fhir.server.controller.*
1212
import org.dtree.fhir.server.services.form.LocalResourceFetcher
1313
import org.dtree.fhir.server.services.form.ResourceFetcher
1414
import org.dtree.fhir.server.services.form.ResponseGenerator
15-
import org.koin.core.module.dsl.singleOf
16-
import org.dtree.fhir.server.controller.StatsController
17-
import org.dtree.fhir.server.controller.StatsControllerImpl
18-
import org.dtree.fhir.server.controller.TracingController
19-
import org.dtree.fhir.server.controller.TracingControllerImpl
2015
import org.koin.core.module.dsl.bind
16+
import org.koin.core.module.dsl.singleOf
2117
import org.koin.dsl.module
2218

2319
object ModulesInjection {

0 commit comments

Comments
 (0)