Skip to content

Commit 6749ebb

Browse files
committed
support adding modules after actor system is created #32
1 parent eeb5552 commit 6749ebb

File tree

3 files changed

+74
-14
lines changed

3 files changed

+74
-14
lines changed

src/main/scala/com/github/jw3/di/InjectExt.scala

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,41 +17,50 @@ import scala.collection.mutable
1717
*
1818
* @param injector top level Guice Injector
1919
*/
20-
class InjectExtImpl(val injector: Injector) extends Extension
21-
22-
object InjectExt extends ExtensionId[InjectExtImpl] with ExtensionIdProvider with InjectExtBuilder {
20+
class InjectExtImpl(modules: Seq[Module]) extends Extension with InjectExtBuilder {
2321
private val manualModules: ThreadLocal[mutable.Set[Module]] = ThreadLocal.withInitial(
2422
new java.util.function.Supplier[mutable.Set[Module]] {
2523
override def get(): mutable.Set[Module] = mutable.Set[Module]()
26-
})
24+
}
25+
)
26+
private var acceptingModules: Boolean = true
2727

2828
/**
29-
* Manually add modules to the injector
30-
* All modules must be added prior to creating the ActorSystem
31-
*/
32-
def addModules(m: Module*): InjectExtBuilder = {
29+
* Manually add modules to the injector
30+
* All modules must be added prior to creating the ActorSystem
31+
*/
32+
def addModules(m: Module*): InjectExtImpl = {
33+
if(!acceptingModules) throw new UnsupportedOperationException("injector was already initialized")
3334
manualModules.set(manualModules.get() ++ m)
3435
this
3536
}
3637

38+
lazy val injector: Injector = {
39+
acceptingModules = false
40+
try Guice.createInjector(modules ++ manualModules.get: _*)
41+
finally manualModules.remove()
42+
}
43+
}
44+
45+
object InjectExt extends ExtensionId[InjectExtImpl] with ExtensionIdProvider {
46+
3747
// internals \\
3848

3949
override def createExtension(sys: ExtendedActorSystem) = {
4050
import InjectExtBuilder._
4151

4252
val config = sys.settings.config
4353
val modules = config.getAs[String](ModuleDiscoveryModeKey).getOrElse(DefaultModuleDiscoveryModeMode) match {
44-
case ManualModuleDiscovery => manualModules.get().toList
54+
case ManualModuleDiscovery => List()
4555
case CfgModuleDiscovery => config.getAs[Seq[String]](CfgModuleDiscoveryKey).map(_.map(strToModule)).getOrElse(Seq()).toList
4656
case SpiModuleDiscovery => ServiceLoader.load(classOf[Module]).iterator.asScala.toList
4757
case v => throw new IllegalArgumentException(s"invalid $ModuleDiscoveryModeKey value, $v")
4858
}
49-
manualModules.remove()
5059

5160
val finalModules = addCfgModule(config) :: modules
5261
val defaultModules = Seq(Defaults.actorSystem(sys))
53-
val injector = Guice.createInjector(finalModules ++ defaultModules: _*)
54-
new InjectExtImpl(injector)
62+
63+
new InjectExtImpl(finalModules ++ defaultModules)
5564
}
5665

5766
override def lookup() = InjectExt
@@ -69,7 +78,7 @@ object InjectExt extends ExtensionId[InjectExtImpl] with ExtensionIdProvider wit
6978
* Defines the module adding interface on the InjectExt
7079
*/
7180
trait InjectExtBuilder {
72-
this: ExtensionId[_] =>
81+
this: Extension =>
7382
def addModules(m: Module*): InjectExtBuilder
7483
}
7584

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package com.github.jw3.di
2+
3+
import akka.actor.ActorSystem
4+
import akka.testkit.TestKit
5+
import net.codingwell.scalaguice.ScalaModule
6+
import org.scalatest.{Matchers, WordSpecLike}
7+
8+
// issue #32
9+
class LazyManualConfigSpec
10+
extends TestKit(ActorSystem("lazy"))
11+
with WordSpecLike
12+
with Matchers {
13+
14+
"late modules" should {
15+
"be accepted" in {
16+
InjectExt(system).addModules(LateModule1)
17+
InjectExt(system).addModules(LateModule2)
18+
}
19+
20+
"be injected" in {
21+
val foo: String = inject[String] annotated "foo"
22+
foo shouldBe "bar"
23+
24+
val baz: String = inject[String] annotated "baz"
25+
baz shouldBe "bash"
26+
}
27+
28+
"be rejected once inited" in {
29+
intercept[UnsupportedOperationException] {
30+
InjectExt(system).addModules(LateModule3)
31+
}
32+
}
33+
}
34+
}
35+
36+
object LateModule1 extends ScalaModule {
37+
override def configure(): Unit = {
38+
bind[String].annotatedWithName("foo").toInstance("bar")
39+
}
40+
}
41+
42+
object LateModule2 extends ScalaModule {
43+
override def configure(): Unit = {
44+
bind[String].annotatedWithName("baz").toInstance("bash")
45+
}
46+
}
47+
48+
object LateModule3 extends ScalaModule {
49+
override def configure(): Unit = ()
50+
}

src/test/scala/com/github/jw3/di/test/InjectSpec.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,9 @@ trait InjectSpec extends WordSpecLike {
2222
*/
2323
def injectTest(name: String, mods: => Seq[Module] = Seq(), cfg: Option[Config] = None)(test: ActorSystem => Unit): Unit = {
2424
registerTest(name) {
25-
InjectExt.addModules(mods: _*)
2625
val sys = ActorSystem(s"${name.replaceAll( """\W""", "_")}", cfg)
26+
InjectExt(sys).addModules(mods: _*)
27+
2728
try test(sys)
2829
finally {Await.ready(sys.terminate(), Duration.create("10s"))}
2930
}

0 commit comments

Comments
 (0)