Shutting down / Closing Kodein context - kotlin

Say in my application I maintain a few Kodein contexts and there are some shared resources in the Kodein contexts which I would like to close when the context it belongs to is no longer needed.
Below is a simple illustration of the problem:
class SomeConnectionPool: Closeable {
override fun close() { /* some operation */ }
}
class SomeResource: Closeable {
override fun close() { /* some operation */ }
}
class SomeService(val pool: SomeConnectionPool) {
fun doStuff() { /* some operation */ }
}
class SomeOtherService(val pool: SomeConnectionPool) {
fun doOtherStuff() { /* some operation */ }
}
val kodein = Kodein {
bind<SomeConnectionPool>() with singleton { SomeConnectionPool() }
bind<SomeResource>() with singleton { SomeResource() }
bind<SomeService>() with singleton { SomeService(instance()) }
bind<SomeOtherService>() with singleton { SomeOtherService(instance()) }
}
fun main(args: Array<String>) {
val service: SomeService by kodein.instance()
service.doStuff()
// this will initialize everything even the unused bindings
val resources by kodein.allInstances<Closeable>()
resources.forEach { it.close() }
}
Ideally there are several properties that should be achieved:
Only retrieve the Closeable instances that are already initialized and close them
SomeService and SomeOtherService should not be responsible of closing SomeConnectionPool since they did not create the instance. They also do not know whether the pool is still being used by something else.
I also considered to retrieve only initialized bindings from kodein.container, but there seems to be no apparent way to do so.

Kodein 5.1 has you covered.
Have a look at:
http://kodein.org/Kodein-DI/?5.1/core#scope-closeable
http://kodein.org/Kodein-DI/?5.1/android#_android_scopes

Related

Ktor / Kodein - How to write Integration Tests

Currently I write a small demo-app which uses Ktor as its Application Environment and Kodein as the Dependency Injection Framework.
During the initialization of the Application I do import some modules, one of those I would like to replace during the initialization of the Integration Tests:
fun Application.module(testing: Boolean = false) {
logger.debug { "Starting main" }
restModule()
di {
bind<Json>() with singleton {
Json {
...
}
}
import(persistenceModule)
}
In the test, I would like to use a different persistenceModule, say eg. a MemoryModule. My tests are initialized like:
fun start() {
val configPath = ClassLoader.getSystemResource("application-acceptanceTest.conf").file
engine = embeddedServer(CIO, commandLineEnvironment(arrayOf("-config=$configPath")))
engine.start()
val disposable = engine.environment.monitor.subscribe(ApplicationStarted) { application: Application ->
started = true
}
while (!started) {
Thread.sleep(10)
}
disposable.dispose()
}
I have tried already to call
engine.application.di
but this gives me (quite obviously) only access to the Ktor Feature, which is already initialized. Is anything like this possible at all?
Kodein-DI allows you to override dependencies. Regarding the following interface:
interface Repository {
fun save()
fun find()
}
You can have a production implementation, included in its own DI module:
class PersistenceRepository : Repository {
/* implementation */
}
val persistenceModule = DI.Module("persistenceModule") {
bind<Repository>() with singleton { PersistenceRepository() }
}
and also a test implementation of that same interface:
class MemoryRepository : Repository {
/* implementation */
}
val memoryModule = DI.Module("memoryModule") {
bind<Repository>(overrides = true) with singleton { MemoryRepository() }
}
Note the overrides parameter that needs to be explicit.
You can pass a DI container to your Ktor function:
val mainDI = DI {
import(persistenceModule)
}
fun Application.main(di: DI) {
di { extend(di) }
}
And extend the mainDI in your tests, to override the proper bindings with the memoryModule:
class ApplicationTest {
val testDI = DI {
extend(mainDI)
import(memoryModule, allowOverride = true)
}
#Test
fun myTest() {
withTestApplication({ main(testDI) })
// ...
}
}

Kotlin fallback wrapper

I'm looking for an elegant solution to the following.
I'd like to implement a Wrapper class that:
Accepts 2 implementations of the same Interface, and returns a new instance of that same Interface.
Any method call to the Wrapper object, tries to call the same method on the 1st implementation.
If the first call results into UnsupportedOperationException, then the 2th implementation should be used instead.
interface API {
fun getData(): String
}
class Main: API {
override fun getData(): String {
throw UnsupportedOperationException()
}
}
class Fallback: API {
override fun getData(): String {
return "data"
}
}
class Wrapper {
companion object {
fun getInstance(main: API, fallback: API): API {
// TODO
}
}
}
class Test {
#Test
fun `invokes the fallback instance`() {
val wrapper = Wrapper.getInstance(Main(), Fallback())
val response = wrapper.getData()
assertEquals(response, "data")
}
}
The best thing I have come up with so far is Delegate with Overrides:
class Wrapper(fallback: API): API by Main() {
val fallback = fallback
override fun getData(): String {
return fallback.getData()
}
}
What I don't like about this solution is that:
It requires overriding each unsupported operation
It gets quite verbose as the Interface grows into a complex multilevel structure with more sub interfaces
I'd also like to avoid Reflection for performance reasons and because this is a Kotlin Multiplatform project.
Any suggestions are appreciated.
Thanks,
Juan
Your proposed solution won't work because it will always favor the fallback for any overridden function.
There's no solution for your needs that can avoid having to manually handle every function of your interface. But you can have an intermediate function that handles the cascading selection of implementation for functions with the same signature.
class Wrapper (private val delegates: Array<out API>): API {
companion object {
fun getInstance(vararg delegates: API) = Wrapper(delegates)
}
private fun <R> delegate0Arg(function: API.() -> R): R {
for (delegate in delegates) {
try {
return delegate.function()
} catch (e: UnsupportedOperationException) {
// continue
}
}
throw UnsupportedOperationException()
}
override val name: String get() = delegate0Arg(API::name)
override fun getData(): String = delegate0Arg(API::getData)
}
But you would need additional functions to handle each unique number of arguments the interface functions have.
private fun <T, R> delegate1Arg(t: T, function: API.(t: T) -> R): R {
for (delegate in delegates) {
try {
return delegate.function(t)
} catch (e: UnsupportedOperationException) {
// continue
}
}
throw UnsupportedOperationException()
}
override fun getData(x: String) = delegate1Arg(x, API::getData)

startKoin in KoinTest-class throws "A KoinContext is already started"

I'm using "withTestAppliction" in one of my tests to test if the route works. Before all Tests the DB-Table "cats" should have no entries. To get the DAO I need Koin in this Test but if conflicts with "withTestAppliction" where Koin will also be startet and throws A KoinContext is already started
[Update]
I know I could use something like handleRequest(HttpMethod.Delete, "/cats") but I don't want to expose this Rest-Interface. Not even for testing.
#ExperimentalCoroutinesApi
class CatsTest: KoinTest {
companion object {
#BeforeClass
#JvmStatic fun setup() {
// once per run
startKoin {
modules(appModule)
}
}
#AfterClass
#JvmStatic fun teardown() {
// clean up after this class, leave nothing dirty behind
stopKoin()
}
}
#Before
fun setupTest() = runBlockingTest {
val dao = inject<CatDAO>()
dao.value.deleteAll()
}
#After
fun cleanUp() {
}
#Test
fun testCreateCat() {
withTestApplication({ module(testing = true) }) {
val call = createCat(predictName("Pepples"), 22)
call.response.status().`should be`(HttpStatusCode.Created)
}
}
}
fun TestApplicationEngine.createCat(name: String, age: Int): TestApplicationCall {
return handleRequest(HttpMethod.Post, "/cats") {
addHeader(HttpHeaders.ContentType, ContentType.Application.FormUrlEncoded.toString())
setBody(listOf(
"name" to name,
"age" to age.toString()
).formUrlEncode())
}
}
After test (after withTestApplication()) call KoinContextHandler.get().stopKoin().
Example: https://github.com/comm1x/ktor-boot/blob/master/test/common/common.kt
It looks similar to the issue I faced. The problem was that the module() passed under the withTestApplication() was trying to create the Koin object again. I replaced the module() with specific modules that I had to load for the tests except for the Koin.
Refer - test sample and
application sample
Had the same problem executing multiple tests in a class. After removing the init/startKoin (since it's initialized in the Application when you test with the emulator).
I am not really sure if this is the correct approach, but it kind of works for me and my build server.
#ExperimentalCoroutinesApi
#RunWith(AndroidJUnit4ClassRunner::class) // or JUnit4..
class MyTest : KoinTest {
private val mockedAppModule: Module = module(override = true)
factory { myRepo }
}
#Before
fun setup() {
loadKoinModules(mockedAppModule)
}
#After
fun tearDown() {
unloadKoinModules(mockedAppModule)
}
#Test
fun testSubscriberRegistration() = runBlockingTest { // only needed if you are using supend functions
// test impl...
}
}

CoroutineScope extension function in a different class

I'm trying to use an extension function to CoroutineScope to launch some asynchronous work.
I'm not sure how to call this method from my main class, see below:
class MyService {
fun CoroutineScope.getFoo() = async(IO|Single|Default) { ... }
}
class MyProgram(val service : MyService) : CoroutineScope {
fun main() {
launch {
// Doesn't work, unresloved `service.getFoo`.
val deferred = service.getFoo() getFoo
// Works, but looks a bit odd IMO.
val deferred = with(service) { getFoo() }
deferred.await()
}
}
}
I know I could just move the async {} keyword to my main method, but in this way, the caller would have to decide the scheduler.
The service knows the nature of its work (IO/Computation bound single-threaded?, etc) and I think it should be the one deciding the scheduler.
As far as I understand your intent is to let the service specify the scheduler. Why not split the specification of the scheduler and the decision to run asynchronously?
Let the service function be suspendable and use withContext to specify the scheduler.
And let the caller decide, if the function should run asynchronously.
class MyService {
suspend fun getFoo() = withContext(Dispatchers.IO) {
//work
}
}
abstract class MyProgram(val service: MyService) : CoroutineScope {
fun main() {
launch {
val deferred = async { service.getFoo() }
//some work
deferred.await()
}
}
}
Why not make getFoo a normal function and pass in the scope:
fun getFoo(scope: CoroutineScope) = scope.async {
//work }
}
launch {
service.getFoo(this)
}

Kotlin: How to run service methods in the context of a transaction class?

I'd like to define database calls in service methods, but have them executed in the context of a Transaction class without opening the connection in the service itself so that I can include multiple service calls in the same transaction.
I'm looking for something like this, but can't quite figure it out.
class Transaction {
init { /** Grab connection **/ }
fun doSelect() { ... }
}
class UserService {
fun Transaction.getUser() {
return doSelect()
}
}
fun main (args: Array<String>) {
Transaction() {
UserService().getUser() // INVALID
...
}
}
Is there a way to do this?
I know that I can pass in a transaction instance to the service like so:
class UserService(tx: Transaction) {
fun getUser() {
with(tx) {
doSelect()
}
}
...
fun main (args: Array<String>) {
Transaction() {
UserService(this).getUser()
...
}
}
...but am hoping for a more elegant pattern.
The system works the other way around, so to fix the issue, swap the receivers:
fun main (args: Array<String>) {
UserService().apply {
Transaction().getUser()
}
}