Skip to content

Commit

Permalink
KTOR-1854 "ApplicationEngineEnvironment was not started" when accessi…
Browse files Browse the repository at this point in the history
…ng application before server is started (#2546)
  • Loading branch information
Stexxe authored Jul 9, 2021
1 parent 4c40b10 commit 1f8fabd
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 2 deletions.
6 changes: 6 additions & 0 deletions ktor-server/ktor-server-host-common/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,10 @@ kotlin.sourceSets {
api(project(":ktor-http:ktor-http-cio"))
}
}

val jvmTest by getting {
dependencies {
implementation(project(":ktor-server:ktor-server-test-host"))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ public class ApplicationEngineEnvironmentReloading(
parentCoroutineContext, rootPath, developmentMode = true
)

private var _applicationInstance: Application? = null
private var _applicationInstance: Application? = Application(this)
private var recreateInstance: Boolean = false
private var _applicationClassLoader: ClassLoader? = null
private val applicationInstanceLock = ReentrantReadWriteLock()
private var packageWatchKeys = emptyList<WatchKey>()
Expand Down Expand Up @@ -289,7 +290,13 @@ public class ApplicationEngineEnvironmentReloading(
}

private fun instantiateAndConfigureApplication(currentClassLoader: ClassLoader): Application {
val newInstance = Application(this)
val newInstance = if (recreateInstance || _applicationInstance == null) {
Application(this)
} else {
recreateInstance = true
_applicationInstance!!
}

safeRiseEvent(ApplicationStarting, newInstance)

avoidingDoubleStartup {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,14 @@ package io.ktor.tests.hosts
import com.typesafe.config.*
import io.ktor.application.*
import io.ktor.config.*
import io.ktor.http.*
import io.ktor.response.*
import io.ktor.routing.*
import io.ktor.server.engine.*
import io.ktor.server.testing.*
import io.ktor.util.*
import kotlinx.coroutines.*
import org.slf4j.helpers.*
import kotlin.reflect.*
import kotlin.reflect.jvm.*
import kotlin.test.*
Expand Down Expand Up @@ -459,6 +465,62 @@ class ApplicationEngineEnvironmentReloadingTests {
attributes.put(TestKey, "functionWithDefaultArg")
}
}

@Test
fun `application is available before environment start`() {
val env = dummyEnv()
val app = env.application
env.start()
assertEquals(app, env.application)
}

@Test
fun `completion handler is invoked when attached before environment start`() {
val env = dummyEnv()

var invoked = false
env.application.coroutineContext[Job]?.invokeOnCompletion {
invoked = true
}

env.start()
env.stop()

assertTrue(invoked, "On completion handler wasn't invoked")
}

@Test
fun `interceptor is invoked when added before environment start`() {
val engine = TestApplicationEngine(createTestEnvironment())
engine.application.intercept(ApplicationCallPipeline.Features) {
call.response.header("Custom", "Value")
}
engine.start()

try {
engine.apply {
application.routing {
get("/") {
call.respondText { "Hello" }
}
}

assertEquals("Value", handleRequest(HttpMethod.Get, "/").response.headers["Custom"])
}
} catch (cause: Throwable) {
fail("Failed with an exception: ${cause.message}")
} finally {
engine.stop(0L, 0L)
}
}

private fun dummyEnv() = ApplicationEngineEnvironmentReloading(
classLoader = this::class.java.classLoader,
log = NOPLogger.NOP_LOGGER,
config = MapApplicationConfig(),
connectors = emptyList(),
modules = emptyList()
)
}

fun Application.topLevelExtensionFunction() {
Expand Down

0 comments on commit 1f8fabd

Please sign in to comment.