Skip to content

Instantly share code, notes, and snippets.

@bfreuden
Last active August 24, 2021 13:27
Show Gist options
  • Select an option

  • Save bfreuden/a866b21c5a6342a3ce1ed26aa636f9f6 to your computer and use it in GitHub Desktop.

Select an option

Save bfreuden/a866b21c5a6342a3ce1ed26aa636f9f6 to your computer and use it in GitHub Desktop.
Toothpick question
package bfreuden
import toothpick.InjectConstructor
import toothpick.ProvidesSingleton
import toothpick.ktp.KTP
import toothpick.ktp.binding.bind
import toothpick.ktp.binding.module
import javax.inject.Named
import javax.inject.Provider
import javax.inject.Scope
import javax.inject.Singleton
/*
The app has many shard (2 in this example) having the same object structure.
All objects inside shards are singletons:
- unique database client for all DAOs
- unique FooDAO (injected into the PowerDAO)
! denotes singletons of the shard1 scope.
? denotes singletons of the shard2 scope.
app (scope)
/ \
----------- ------------
/ \
shard1 (scope) shard2 (scope)
/ \ / \
!PowerDAO !FooDAO ?PowerDAO ?FooDAO
/ \ \ / \ \
!FooDAO !DBCLient !DBCLient ?FooDAO ?DBCLient ?DBCLient
| |
!DBCLient ?DBCLient
*/
@Scope
@Retention(AnnotationRetention.RUNTIME)
annotation class ShardSingleton
@ProvidesSingleton
@InjectConstructor
@ShardSingleton
class DBClientProvider(@Named("appConfig")val appConfig: Map<String, String>): Provider<DBClient> {
override fun get() = DBClient(appConfig["dpPort"]!!.toInt())
}
class DBClient(val port: Int)
@Singleton // the FooDAO is not a singleton without this annotation
@ShardSingleton
@InjectConstructor
class FooDAO(val dbClient: DBClient)
@Singleton
@ShardSingleton
@InjectConstructor
class PowerDAO(val dbClient: DBClient, val fooDAO: FooDAO)
@Singleton
@ShardSingleton
@InjectConstructor
class Shard(@Named("appConfig")val appConfig: Map<String, String>, val fooDAO: FooDAO, val powerDAO: PowerDAO)
@InjectConstructor
class App(@Named("appConfig")val appConfig: Map<String, String>) {
var shard1: Shard = KTP.openScopes("app", "shard1")
.supportScopeAnnotation(ShardSingleton::class.java)
.installModules(module {
// does not work without this explicit binding
bind<DBClient>().toProvider(DBClientProvider::class)
}).getInstance(Shard::class.java)
var shard2: Shard = KTP.openScopes("app", "shard2")
.supportScopeAnnotation(ShardSingleton::class.java)
.installModules(module {
// does not work without this explicit binding
bind<DBClient>().toProvider(DBClientProvider::class)
}).getInstance(Shard::class.java)
}
fun main() {
val app = KTP.openScope("app")
.installModules(
module {
bind<Map<String, String>>().withName("appConfig").toInstance(mapOf("dpPort" to "12"))
}
)
.getInstance(App::class.java)
val shard1 = app.shard1
println("shard1 $shard1")
println("shard1.appConfig ${shard1.appConfig}")
println("shard1.fooDAO ${shard1.fooDAO} (${shard1.fooDAO.dbClient})")
println("shard1.powerDAO ${shard1.powerDAO} (${shard1.powerDAO.fooDAO})")
println("shard1.powerDAO.dbClient ${shard1.powerDAO.dbClient}")
println("shard1.powerDAO.fooDAO ${shard1.powerDAO.fooDAO} (${shard1.powerDAO.fooDAO.dbClient})")
println()
val shard2 = app.shard2
println("shard2 $shard2")
println("shard2.appConfig ${shard2.appConfig}")
println("shard2.fooDAO ${shard2.fooDAO} (${shard2.fooDAO.dbClient})")
println("shard2.powerDAO ${shard2.powerDAO} (${shard2.powerDAO.fooDAO})")
println("shard2.powerDAO.dbClient ${shard2.powerDAO.dbClient}")
println("shard2.powerDAO.fooDAO ${shard2.powerDAO.fooDAO} (${shard2.powerDAO.fooDAO.dbClient})")
}
/*
-------------------
Program output:
=> FooDAO and DBClient are shard singletons
shard1 bfreuden.Shard@6b57696f
shard1.appConfig {dpPort=12}
shard1.fooDAO bfreuden.FooDAO@38bc8ab5 (bfreuden.DBClient@687080dc)
shard1.powerDAO bfreuden.PowerDAO@23d2a7e8 (bfreuden.FooDAO@7a9273a8)
shard1.powerDAO.dbClient bfreuden.DBClient@687080dc
shard1.powerDAO.fooDAO bfreuden.FooDAO@7a9273a8 (bfreuden.DBClient@687080dc)
shard2 bfreuden.Shard@26a7b76d
shard2.appConfig {dpPort=12}
shard2.fooDAO bfreuden.FooDAO@4abdb505 (bfreuden.DBClient@7ce6a65d)
shard2.powerDAO bfreuden.PowerDAO@1500955a (bfreuden.FooDAO@e874448)
shard2.powerDAO.dbClient bfreuden.DBClient@7ce6a65d
shard2.powerDAO.fooDAO bfreuden.FooDAO@e874448 (bfreuden.DBClient@7ce6a65d)
-------------------
Program output without @Singleton annotation on FooDAO:
=> DBClient is a shard singleton
=> FooDAO is not a shard singleton (PowerDAO has its own instance)
shard1 bfreuden.Shard@6b57696f
shard1.appConfig {dpPort=12}
shard1.fooDAO bfreuden.FooDAO@38bc8ab5 (bfreuden.DBClient@687080dc)
shard1.powerDAO bfreuden.PowerDAO@23d2a7e8 (bfreuden.FooDAO@7a9273a8)
shard1.powerDAO.dbClient bfreuden.DBClient@687080dc
shard1.powerDAO.fooDAO bfreuden.FooDAO@7a9273a8 (bfreuden.DBClient@687080dc)
shard2 bfreuden.Shard@26a7b76d
shard2.appConfig {dpPort=12}
shard2.fooDAO bfreuden.FooDAO@4abdb505 (bfreuden.DBClient@7ce6a65d)
shard2.powerDAO bfreuden.PowerDAO@1500955a (bfreuden.FooDAO@e874448)
shard2.powerDAO.dbClient bfreuden.DBClient@7ce6a65d
shard2.powerDAO.fooDAO bfreuden.FooDAO@e874448 (bfreuden.DBClient@7ce6a65d)
-------------------
Program output without bind<DBClient>().toProvider(DBClientProvider::class):
Exception in thread "main" toothpick.locators.NoFactoryFoundException:
No factory could be found for class bfreuden.DBClient.
Check that the class has either a @Inject annotated constructor or contains @Inject annotated members.
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment