diff --git a/akka-actor-tests/src/test/scala/akka/actor/ExtensionSpec.scala b/akka-actor-tests/src/test/scala/akka/actor/ExtensionSpec.scala index 1996020497..90deed788a 100644 --- a/akka-actor-tests/src/test/scala/akka/actor/ExtensionSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/actor/ExtensionSpec.scala @@ -34,16 +34,17 @@ object FailingTestExtension extends ExtensionId[FailingTestExtension] with Exten class TestException extends IllegalArgumentException("ERR") with NoStackTrace } -object InstanceCountingExtension extends ExtensionId[DummyExtensionImpl] with ExtensionIdProvider { +object InstanceCountingExtension extends ExtensionId[InstanceCountingExtension] with ExtensionIdProvider { val createCount = new AtomicInteger(0) - override def createExtension(system: ExtendedActorSystem): DummyExtensionImpl = { - createCount.addAndGet(1) - new DummyExtensionImpl + override def createExtension(system: ExtendedActorSystem): InstanceCountingExtension = { + new InstanceCountingExtension } override def lookup(): ExtensionId[_ <: Extension] = this } -class DummyExtensionImpl extends Extension +class InstanceCountingExtension extends Extension { + InstanceCountingExtension.createCount.incrementAndGet() +} // Dont't place inside ActorSystemSpec object, since it will not be garbage collected and reference to system remains class FailingTestExtension(val system: ExtendedActorSystem) extends Extension { @@ -111,12 +112,33 @@ class ExtensionSpec extends AnyWordSpec with Matchers { shutdownActorSystem(system) } - "allow for auto-loading of library-extensions" in { + "allow for auto-loading of library-extensions from reference.conf" in { + import akka.util.ccompat.JavaConverters._ + // could be initialized by other tests, but assuming tests are not running in parallel + val countBefore = InstanceCountingExtension.createCount.get() val system = ActorSystem("extensions") - val listedExtensions = system.settings.config.getStringList("akka.library-extensions") - listedExtensions.size should be > 0 - // could be initialized by other tests, so at least once - InstanceCountingExtension.createCount.get() should be > 0 + val listedExtensions = system.settings.config.getStringList("akka.library-extensions").asScala + listedExtensions.count(_.contains("InstanceCountingExtension")) should ===(1) + + InstanceCountingExtension.createCount.get() - countBefore should ===(1) + + shutdownActorSystem(system) + } + + "not create duplicate instances when auto-loading of library-extensions" in { + import akka.util.ccompat.JavaConverters._ + // could be initialized by other tests, but assuming tests are not running in parallel + val countBefore = InstanceCountingExtension.createCount.get() + val system = ActorSystem( + "extensions", + ConfigFactory.parseString( + """ + akka.library-extensions = ["akka.actor.InstanceCountingExtension", "akka.actor.InstanceCountingExtension", "akka.actor.InstanceCountingExtension$"] + """)) + val listedExtensions = system.settings.config.getStringList("akka.library-extensions").asScala + listedExtensions.count(_.contains("InstanceCountingExtension")) should ===(3) // testing duplicate names + + InstanceCountingExtension.createCount.get() - countBefore should ===(1) shutdownActorSystem(system) } diff --git a/akka-actor-typed/src/main/resources/reference.conf b/akka-actor-typed/src/main/resources/reference.conf index f3f36a1f40..3cb166b939 100644 --- a/akka-actor-typed/src/main/resources/reference.conf +++ b/akka-actor-typed/src/main/resources/reference.conf @@ -16,7 +16,7 @@ akka.actor.typed { library-extensions = ${?akka.actor.typed.library-extensions} [] # Receptionist is started eagerly to allow clustered receptionist to gather remote registrations early on. - library-extensions += "akka.actor.typed.receptionist.Receptionist" + library-extensions += "akka.actor.typed.receptionist.Receptionist$" # While an actor is restarted (waiting for backoff to expire and children to stop) # incoming messages and signals are stashed, and delivered later to the newly restarted diff --git a/akka-actor/src/main/resources/reference.conf b/akka-actor/src/main/resources/reference.conf index 9d3ba13270..76f4eceef8 100644 --- a/akka-actor/src/main/resources/reference.conf +++ b/akka-actor/src/main/resources/reference.conf @@ -73,7 +73,7 @@ akka { # # Should not be set by end user applications in 'application.conf', use the extensions property for that # - library-extensions = ${?akka.library-extensions} ["akka.serialization.SerializationExtension"] + library-extensions = ${?akka.library-extensions} ["akka.serialization.SerializationExtension$"] # List FQCN of extensions which shall be loaded at actor system startup. # Should be on the format: 'extensions = ["foo", "bar"]' etc. diff --git a/akka-actor/src/main/scala/akka/actor/ActorSystem.scala b/akka-actor/src/main/scala/akka/actor/ActorSystem.scala index d7e358a452..ea63ad7591 100644 --- a/akka-actor/src/main/scala/akka/actor/ActorSystem.scala +++ b/akka-actor/src/main/scala/akka/actor/ActorSystem.scala @@ -1178,9 +1178,11 @@ private[akka] class ActorSystemImpl( * when the extension cannot be found at all we throw regardless of this setting) */ def loadExtensions(key: String, throwOnLoadFail: Boolean): Unit = { + immutableSeq(settings.config.getStringList(key)).foreach { fqcn => dynamicAccess.getObjectFor[AnyRef](fqcn).recoverWith { - case _ => dynamicAccess.createInstanceFor[AnyRef](fqcn, Nil) + case firstProblem => + dynamicAccess.createInstanceFor[AnyRef](fqcn, Nil).recoverWith { case _ => Failure(firstProblem) } } match { case Success(p: ExtensionIdProvider) => registerExtension(p.lookup()) diff --git a/akka-stream/src/main/resources/reference.conf b/akka-stream/src/main/resources/reference.conf index 8cf220c6c8..dd982714a1 100644 --- a/akka-stream/src/main/resources/reference.conf +++ b/akka-stream/src/main/resources/reference.conf @@ -3,7 +3,7 @@ ##################################### # eager creation of the system wide materializer -akka.library-extensions += "akka.stream.SystemMaterializer" +akka.library-extensions += "akka.stream.SystemMaterializer$" akka { stream {