
jvmMain.it.unibo.alchemist.boundary.launchers.GraphQLServer.kt Maven / Gradle / Ivy
/*
* Copyright (C) 2010-2023, Danilo Pianini and contributors
* listed, for each module, in the respective subproject's build.gradle.kts file.
*
* This file is part of Alchemist, and is distributed under the terms of the
* GNU General Public License, with a linking exception,
* as described in the file LICENSE in the Alchemist distribution's top directory.
*/
package it.unibo.alchemist.boundary.launchers
import io.ktor.server.engine.ApplicationEngine
import io.ktor.server.engine.embeddedServer
import io.ktor.server.engine.stop
import io.ktor.server.netty.Netty
import it.unibo.alchemist.boundary.OutputMonitor
import it.unibo.alchemist.boundary.graphql.monitor.EnvironmentSubscriptionMonitor
import it.unibo.alchemist.boundary.graphql.server.modules.graphQLModule
import it.unibo.alchemist.boundary.graphql.server.modules.graphQLRoutingModule
import it.unibo.alchemist.boundary.graphql.utils.DefaultGraphQLSettings
import it.unibo.alchemist.model.Environment
import it.unibo.alchemist.model.Position
import it.unibo.alchemist.model.Time
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.sync.Semaphore
/**
* An [OutputMonitor] observing the [environment] through a GraphQL server listening on [host]:[port].
* The server is started in a new coroutine on the [serverDispatcher] dispatcher.
* By default, the server is stopped after the simulation terminates.
* This behavior can be changed by setting [teardownOnSimulationTermination] to false.
*/
class GraphQLServer> @JvmOverloads constructor(
val environment: Environment,
val host: String = DefaultGraphQLSettings.DEFAULT_HOST,
val port: Int = DefaultGraphQLSettings.DEFAULT_PORT,
val teardownOnSimulationTermination: Boolean = true,
private val serverDispatcher: CoroutineDispatcher = Dispatchers.Default,
) : OutputMonitor {
private val subscriptionMonitor = EnvironmentSubscriptionMonitor()
private lateinit var server: ApplicationEngine
override fun initialized(environment: Environment) {
environment.simulation.addOutputMonitor(subscriptionMonitor)
server = makeServer()
val mutex = java.util.concurrent.Semaphore(0)
Thread(
{
runBlocking {
launch(serverDispatcher) {
mutex.release()
server.start(wait = true)
}
}
},
"alchemist-graphql-server@$host:$port",
).start()
mutex.acquireUninterruptibly()
}
override fun finished(environment: Environment, time: Time, step: Long) {
if (teardownOnSimulationTermination) {
server.stop()
}
}
private fun makeServer(): ApplicationEngine =
embeddedServer(
Netty,
port = port,
host = host,
module = {
graphQLModule([email protected])
graphQLRoutingModule()
},
)
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy