diff --git a/akka-actor-tests/src/test/scala/akka/actor/ProviderSelectionSpec.scala b/akka-actor-tests/src/test/scala/akka/actor/ProviderSelectionSpec.scala new file mode 100644 index 0000000000..967edd4249 --- /dev/null +++ b/akka-actor-tests/src/test/scala/akka/actor/ProviderSelectionSpec.scala @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2019 Lightbend Inc. + */ + +package akka.actor + +import akka.actor.ActorSystem.Settings +import akka.actor.ActorSystem.findClassLoader +import akka.actor.setup.ActorSystemSetup +import akka.testkit.AbstractSpec +import com.typesafe.config.ConfigFactory + +class ProviderSelectionSpec extends AbstractSpec { + import ProviderSelection.{ ClusterActorRefProvider, RemoteActorRefProvider } + + "ProviderSelection" must { + + val setup = ActorSystemSetup() + val localConfig = ConfigFactory.load() + val classLoader = findClassLoader() + + def settingsWith(key: String): Settings = { + val c = ConfigFactory.parseString(s"""akka.actor.provider = "$key"""").withFallback(localConfig) + new Settings(classLoader, c, "test", setup) + } + + "create a Local ProviderSelection and set local provider fqcn in Settings" in { + val ps = ProviderSelection.Local + ps.fqcn shouldEqual classOf[LocalActorRefProvider].getName + ps.hasCluster shouldBe false + settingsWith("local").ProviderClass shouldEqual ps.fqcn + } + + "create a Remote ProviderSelection and set remote provider fqcn in Settings" in { + val ps = ProviderSelection.Remote + ps.fqcn shouldEqual RemoteActorRefProvider + ps.hasCluster shouldBe false + ProviderSelection("remote") shouldEqual ProviderSelection(RemoteActorRefProvider) + settingsWith("remote").ProviderClass shouldEqual ps.fqcn + } + + "create a Cluster ProviderSelection and set cluster provider fqcn in Settings" in { + val ps = ProviderSelection.Cluster + ps.fqcn shouldEqual ClusterActorRefProvider + ps.hasCluster shouldBe true + ProviderSelection("cluster") shouldEqual ProviderSelection(ClusterActorRefProvider) + settingsWith("cluster").ProviderClass shouldEqual ps.fqcn + } + + "create a Custom ProviderSelection and set custom provider fqcn in Settings" in { + val other = "other.ActorRefProvider" + val ps = ProviderSelection.Custom(other) //checked by dynamicAccess + ps.fqcn shouldEqual "other.ActorRefProvider" + ps.hasCluster shouldBe false + settingsWith(other).ProviderClass shouldEqual ps.fqcn + } + } +} diff --git a/akka-actor-typed/src/main/scala/akka/actor/typed/receptionist/Receptionist.scala b/akka-actor-typed/src/main/scala/akka/actor/typed/receptionist/Receptionist.scala index 0b00037945..0df812d497 100644 --- a/akka-actor-typed/src/main/scala/akka/actor/typed/receptionist/Receptionist.scala +++ b/akka-actor-typed/src/main/scala/akka/actor/typed/receptionist/Receptionist.scala @@ -27,15 +27,9 @@ abstract class Receptionist extends Extension { */ @InternalApi private[akka] class ReceptionistImpl(system: ActorSystem[_]) extends Receptionist { - private def hasCluster: Boolean = { - // FIXME: replace with better indicator that cluster is enabled - val provider = system.settings.untypedSettings.ProviderClass - provider == "akka.cluster.ClusterActorRefProvider" - } - override val ref: ActorRef[Receptionist.Command] = { val provider: ReceptionistBehaviorProvider = - if (hasCluster) { + if (system.settings.untypedSettings.ProviderSelectionType.hasCluster) { system.dynamicAccess .getObjectFor[ReceptionistBehaviorProvider]("akka.cluster.typed.internal.receptionist.ClusterReceptionist") .recover { diff --git a/akka-actor/src/main/mima-filters/2.5.x.backwards.excludes b/akka-actor/src/main/mima-filters/2.5.x.backwards.excludes index d06ee92de5..4b7f83a48f 100644 --- a/akka-actor/src/main/mima-filters/2.5.x.backwards.excludes +++ b/akka-actor/src/main/mima-filters/2.5.x.backwards.excludes @@ -43,6 +43,9 @@ ProblemFilters.exclude[MissingTypesProblem]("akka.dispatch.BalancingDispatcher$S ProblemFilters.exclude[MissingTypesProblem]("akka.dispatch.MonitorableThreadFactory$AkkaForkJoinWorkerThread") ProblemFilters.exclude[IncompatibleMethTypeProblem]("akka.dispatch.MonitorableThreadFactory#AkkaForkJoinWorkerThread.this") +# Better indicator of ActorRefProvider implementation selection #27009 +ProblemFilters.exclude[DirectMissingMethodProblem]("akka.actor.ProviderSelection.this") + # Remove deprecated features since 2.5.0 https://github.com/akka/akka/issues/26492 # The ActorCell and others are due to UntypedActorContext being removed from the Cell's hierarchy ProblemFilters.exclude[MissingClassProblem]("akka.actor.UntypedActor") diff --git a/akka-actor/src/main/scala/akka/actor/ActorSystem.scala b/akka-actor/src/main/scala/akka/actor/ActorSystem.scala index 302e5f449a..f1dad0cb39 100644 --- a/akka-actor/src/main/scala/akka/actor/ActorSystem.scala +++ b/akka-actor/src/main/scala/akka/actor/ActorSystem.scala @@ -82,11 +82,23 @@ object BootstrapSetup { } -abstract class ProviderSelection private (private[akka] val identifier: String) +/** + * @param identifier the simple name of the selected provider + * @param fqcn the fully-qualified class name of the selected provider + */ +abstract class ProviderSelection private ( + private[akka] val identifier: String, + private[akka] val fqcn: String, + private[akka] val hasCluster: Boolean) object ProviderSelection { - case object Local extends ProviderSelection("local") - case object Remote extends ProviderSelection("remote") - case object Cluster extends ProviderSelection("cluster") + private[akka] val RemoteActorRefProvider = "akka.remote.RemoteActorRefProvider" + private[akka] val ClusterActorRefProvider = "akka.cluster.ClusterActorRefProvider" + + case object Local extends ProviderSelection("local", classOf[LocalActorRefProvider].getName, hasCluster = false) + // these two cannot be referenced by class as they may not be on the classpath + case object Remote extends ProviderSelection("remote", RemoteActorRefProvider, hasCluster = false) + case object Cluster extends ProviderSelection("cluster", ClusterActorRefProvider, hasCluster = true) + final case class Custom(override val fqcn: String) extends ProviderSelection("custom", fqcn, hasCluster = false) /** * JAVA API @@ -103,6 +115,15 @@ object ProviderSelection { */ def cluster(): ProviderSelection = Cluster + /** INTERNAL API */ + @InternalApi private[akka] def apply(providerClass: String): ProviderSelection = + providerClass match { + case "local" => Local + // additional fqcn for older configs not using 'remote' or 'cluster' + case "remote" | RemoteActorRefProvider => Remote + case "cluster" | ClusterActorRefProvider => Cluster + case fqcn => Custom(fqcn) + } } /** @@ -331,18 +352,16 @@ object ActorSystem { import config._ final val ConfigVersion: String = getString("akka.version") - final val ProviderClass: String = - setup - .get[BootstrapSetup] - .flatMap(_.actorRefProvider) - .map(_.identifier) - .getOrElse(getString("akka.actor.provider")) match { - case "local" => classOf[LocalActorRefProvider].getName - // these two cannot be referenced by class as they may not be on the classpath - case "remote" => "akka.remote.RemoteActorRefProvider" - case "cluster" => "akka.cluster.ClusterActorRefProvider" - case fqcn => fqcn - } + + private final val providerSelectionSetup = setup + .get[BootstrapSetup] + .flatMap(_.actorRefProvider) + .map(_.identifier) + .getOrElse(getString("akka.actor.provider")) + + final val ProviderSelectionType: ProviderSelection = ProviderSelection(providerSelectionSetup) + + final val ProviderClass: String = ProviderSelectionType.fqcn final val SupervisorStrategyClass: String = getString("akka.actor.guardian-supervisor-strategy") final val CreationTimeout: Timeout = Timeout(config.getMillisDuration("akka.actor.creation-timeout")) diff --git a/akka-testkit/src/test/scala/akka/testkit/AbstractSpec.scala b/akka-testkit/src/test/scala/akka/testkit/AbstractSpec.scala new file mode 100644 index 0000000000..8d0edc69ef --- /dev/null +++ b/akka-testkit/src/test/scala/akka/testkit/AbstractSpec.scala @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2019 Lightbend Inc. + */ + +package akka.testkit + +import org.scalatest.BeforeAndAfterEach +import org.scalatest.Matchers +import org.scalatest.WordSpecLike + +// we could migrate AkkaSpec to extend this +abstract class AbstractSpec extends WordSpecLike with Matchers with BeforeAndAfterEach