Align SpawnProtocol with style guide #27318

This commit is contained in:
Johan Andrén 2019-07-16 11:36:12 +02:00 committed by GitHub
parent 511356177d
commit d03294d359
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 67 additions and 60 deletions

View file

@ -31,20 +31,21 @@ public class SpawnProtocolDocTest {
public abstract static class HelloWorldMain {
private HelloWorldMain() {}
public static final Behavior<SpawnProtocol> main =
public static final Behavior<SpawnProtocol.Command> main =
Behaviors.setup(
context -> {
// Start initial tasks
// context.spawn(...)
return SpawnProtocol.behavior();
return SpawnProtocol.create();
});
}
// #main
public static void main(String[] args) throws Exception {
// #system-spawn
final ActorSystem<SpawnProtocol> system = ActorSystem.create(HelloWorldMain.main, "hello");
final ActorSystem<SpawnProtocol.Command> system =
ActorSystem.create(HelloWorldMain.main, "hello");
final Duration timeout = Duration.ofSeconds(3);
CompletionStage<ActorRef<HelloWorld.Greet>> greeter =

View file

@ -11,6 +11,8 @@ import akka.actor.typed.scaladsl.Behaviors
import akka.util.Timeout
import org.scalatest.{ Matchers, WordSpec, WordSpecLike }
import scala.concurrent.Future
object SpawnProtocolSpec {
sealed trait Message
final case class Ping(replyTo: ActorRef[Pong.type]) extends Message
@ -32,7 +34,7 @@ class SpawnProtocolSpec extends ScalaTestWithActorTestKit with WordSpecLike {
"Spawn behavior" must {
"spawn child actor" in {
val parentReply = TestProbe[ActorRef[Message]]()
val parent = spawn(SpawnProtocol.behavior, "parent")
val parent = spawn(SpawnProtocol(), "parent")
parent ! SpawnProtocol.Spawn(target, "child", Props.empty, parentReply.ref)
val child = parentReply.receiveMessage()
child.path.name should ===("child")
@ -43,17 +45,18 @@ class SpawnProtocolSpec extends ScalaTestWithActorTestKit with WordSpecLike {
}
"have nice API for ask" in {
val parent = spawn(SpawnProtocol.behavior, "parent2")
val parent = spawn(SpawnProtocol(), "parent2")
import akka.actor.typed.scaladsl.AskPattern._
implicit val timeout = Timeout(5.seconds)
val parentReply = parent.ask(SpawnProtocol.Spawn(target, "child", Props.empty))
val parentReply: Future[ActorRef[Ping]] =
parent.ask(SpawnProtocol.Spawn(target, "child", Props.empty, _))
val child = parentReply.futureValue
val childReply = TestProbe[Pong.type]()
child ! Ping(childReply.ref)
}
"be possible to use as guardian behavior" in {
val sys = ActorSystem(SpawnProtocol.behavior, "SpawnProtocolSpec2")
val sys = ActorSystem(SpawnProtocol(), "SpawnProtocolSpec2")
try {
val guardianReply = TestProbe[ActorRef[Message]]()(sys)
sys ! SpawnProtocol.Spawn(target, "child1", Props.empty, guardianReply.ref)
@ -70,7 +73,7 @@ class SpawnProtocolSpec extends ScalaTestWithActorTestKit with WordSpecLike {
"spawn with unique name when given name is taken" in {
val parentReply = TestProbe[ActorRef[Message]]()
val parent = spawn(SpawnProtocol.behavior, "parent3")
val parent = spawn(SpawnProtocol(), "parent3")
parent ! SpawnProtocol.Spawn(target, "child", Props.empty, parentReply.ref)
val child0 = parentReply.receiveMessage()
@ -101,7 +104,7 @@ class StubbedSpawnProtocolSpec extends WordSpec with Matchers {
"spawn with given name" in {
val parentReply = TestInbox[ActorRef[Message]]()
val testkit = BehaviorTestKit(SpawnProtocol.behavior)
val testkit = BehaviorTestKit(SpawnProtocol())
testkit.run(SpawnProtocol.Spawn(target, "child", Props.empty, parentReply.ref))
val child = parentReply.receiveMessage()
child.path.name should ===("child")
@ -110,7 +113,7 @@ class StubbedSpawnProtocolSpec extends WordSpec with Matchers {
"spawn anonymous when name undefined" in {
val parentReply = TestInbox[ActorRef[Message]]()
val testkit = BehaviorTestKit(SpawnProtocol.behavior)
val testkit = BehaviorTestKit(SpawnProtocol())
testkit.run(SpawnProtocol.Spawn(target, "", Props.empty, parentReply.ref))
val child = parentReply.receiveMessage()
child.path.name should startWith("$")

View file

@ -55,7 +55,7 @@ class DispatcherSelectorSpec extends ScalaTestWithActorTestKit(DispatcherSelecto
}
"select same dispatcher as parent" in {
val parent = spawn(SpawnProtocol.behavior, Props.empty.withDispatcherFromConfig("ping-pong-dispatcher"))
val parent = spawn(SpawnProtocol(), Props.empty.withDispatcherFromConfig("ping-pong-dispatcher"))
val childProbe = createTestProbe[ActorRef[Ping]]()
parent ! SpawnProtocol.Spawn(PingPong(), "child", Props.empty.withDispatcherSameAsParent, childProbe.ref)
@ -68,10 +68,10 @@ class DispatcherSelectorSpec extends ScalaTestWithActorTestKit(DispatcherSelecto
}
"select same dispatcher as parent, several levels" in {
val grandParent = spawn(SpawnProtocol.behavior, Props.empty.withDispatcherFromConfig("ping-pong-dispatcher"))
val grandParent = spawn(SpawnProtocol(), Props.empty.withDispatcherFromConfig("ping-pong-dispatcher"))
val parentProbe = createTestProbe[ActorRef[SpawnProtocol.Spawn[Ping]]]()
grandParent ! SpawnProtocol.Spawn(
SpawnProtocol.behavior,
SpawnProtocol(),
"parent",
Props.empty.withDispatcherSameAsParent,
parentProbe.ref)

View file

@ -15,6 +15,8 @@ import akka.actor.testkit.typed.scaladsl.ScalaTestWithActorTestKit
import com.typesafe.config.ConfigFactory
import org.scalatest.WordSpecLike
import scala.concurrent.Future
object DispatchersDocSpec {
val config = ConfigFactory.parseString("""
@ -60,19 +62,22 @@ class DispatchersDocSpec extends ScalaTestWithActorTestKit(DispatchersDocSpec.co
"Actor Dispatchers" should {
"support default and blocking dispatcher" in {
val probe = TestProbe[Dispatcher]()
val actor: ActorRef[SpawnProtocol] = spawn(SpawnProtocol.behavior)
val actor: ActorRef[SpawnProtocol.Command] = spawn(SpawnProtocol())
val withDefault = actor.ask(Spawn(giveMeYourDispatcher, "default", Props.empty)).futureValue
withDefault ! WhichDispatcher(probe.ref)
val withDefault: Future[ActorRef[WhichDispatcher]] =
actor.ask(Spawn(giveMeYourDispatcher, "default", Props.empty, _))
withDefault.futureValue ! WhichDispatcher(probe.ref)
probe.receiveMessage().id shouldEqual "akka.actor.default-dispatcher"
val withBlocking = actor.ask(Spawn(giveMeYourDispatcher, "default", DispatcherSelector.blocking())).futureValue
withBlocking ! WhichDispatcher(probe.ref)
val withBlocking: Future[ActorRef[WhichDispatcher]] =
actor.ask(Spawn(giveMeYourDispatcher, "default", DispatcherSelector.blocking(), _))
withBlocking.futureValue ! WhichDispatcher(probe.ref)
probe.receiveMessage().id shouldEqual "akka.actor.default-blocking-io-dispatcher"
val withCustom =
actor.ask(Spawn(giveMeYourDispatcher, "default", DispatcherSelector.fromConfig("your-dispatcher"))).futureValue
withCustom ! WhichDispatcher(probe.ref)
val withCustom: Future[ActorRef[WhichDispatcher]] =
actor.ask(Spawn(giveMeYourDispatcher, "default", DispatcherSelector.fromConfig("your-dispatcher"), _))
withCustom.futureValue ! WhichDispatcher(probe.ref)
probe.receiveMessage().id shouldEqual "your-dispatcher"
}
}

View file

@ -32,12 +32,12 @@ object SpawnProtocolDocSpec {
//#main
object HelloWorldMain {
val main: Behavior[SpawnProtocol] =
val main: Behavior[SpawnProtocol.Command] =
Behaviors.setup { context =>
// Start initial tasks
// context.spawn(...)
SpawnProtocol.behavior
SpawnProtocol()
}
}
//#main
@ -51,7 +51,7 @@ class SpawnProtocolDocSpec extends ScalaTestWithActorTestKit with WordSpecLike {
"be able to spawn actors" in {
//#system-spawn
val system: ActorSystem[SpawnProtocol] =
val system: ActorSystem[SpawnProtocol.Command] =
ActorSystem(HelloWorldMain.main, "hello")
// needed in implicit scope for ask (?)
@ -61,7 +61,7 @@ class SpawnProtocolDocSpec extends ScalaTestWithActorTestKit with WordSpecLike {
implicit val scheduler: Scheduler = system.scheduler
val greeter: Future[ActorRef[HelloWorld.Greet]] =
system.ask(SpawnProtocol.Spawn(behavior = HelloWorld(), name = "greeter", props = Props.empty))
system.ask(SpawnProtocol.Spawn(behavior = HelloWorld(), name = "greeter", props = Props.empty, _))
val greetedBehavior = Behaviors.receive[HelloWorld.Greeted] { (context, message) =>
context.log.info("Greeting for {} from {}", message.whom, message.from)
@ -69,7 +69,7 @@ class SpawnProtocolDocSpec extends ScalaTestWithActorTestKit with WordSpecLike {
}
val greetedReplyTo: Future[ActorRef[HelloWorld.Greeted]] =
system.ask(SpawnProtocol.Spawn(greetedBehavior, name = "", props = Props.empty))
system.ask(SpawnProtocol.Spawn(greetedBehavior, name = "", props = Props.empty, _))
for (greeterRef <- greeter; replyToRef <- greetedReplyTo) {
greeterRef ! HelloWorld.Greet("Akka", replyToRef)

View file

@ -5,25 +5,29 @@
package akka.actor.typed
import scala.annotation.tailrec
import akka.actor.typed.scaladsl.Behaviors
import akka.annotation.DoNotInherit
/**
* A message protocol for actors that support spawning a child actor when receiving a [[SpawnProtocol#Spawn]]
* message and sending back the [[ActorRef]] of the child actor. Create instances through the [[SpawnProtocol#apply]]
* or [[SpawnProtocol.create()]] factory methods.
*
* The typical usage of this is to use it as the guardian actor of the [[ActorSystem]], possibly combined with
* `Behaviors.setup` to starts some initial tasks or actors. Child actors can then be started from the outside
* by telling or asking [[SpawnProtocol#Spawn]] to the actor reference of the system. When using `ask` this is
* similar to how [[akka.actor.ActorSystem#actorOf]] can be used in untyped actors with the difference that
* a `Future` / `CompletionStage` of the `ActorRef` is returned.
*
* Stopping children is done through specific support in the protocol of the children, or stopping the entire
* spawn protocol actor.
*/
object SpawnProtocol {
object Spawn {
/**
* Special factory to make using Spawn with ask easier
*/
def apply[T](behavior: Behavior[T], name: String, props: Props): ActorRef[ActorRef[T]] => Spawn[T] =
replyTo => new Spawn(behavior, name, props, replyTo)
/**
* Special factory to make using Spawn with ask easier. Props defaults to Props.empty
*/
def apply[T](behavior: Behavior[T], name: String): ActorRef[ActorRef[T]] => Spawn[T] =
replyTo => new Spawn(behavior, name, Props.empty, replyTo)
}
/**
* Not for user extension
*/
@DoNotInherit sealed trait Command
/**
* Spawn a child actor with the given `behavior` and send back the `ActorRef` of that child to the given
@ -37,12 +41,17 @@ object SpawnProtocol {
* `InvalidActorNameException`, but it's better to use unique names to begin with.
*/
final case class Spawn[T](behavior: Behavior[T], name: String, props: Props, replyTo: ActorRef[ActorRef[T]])
extends SpawnProtocol
extends Command
/**
* Behavior implementing the [[SpawnProtocol]].
* Java API: returns a behavior that can be commanded to spawn arbitrary children.
*/
val behavior: Behavior[SpawnProtocol] =
def create(): Behavior[Command] = apply()
/**
* Scala API: returns a behavior that can be commanded to spawn arbitrary children.
*/
def apply(): Behavior[Command] =
Behaviors.receive { (ctx, msg) =>
msg match {
case Spawn(bhvr, name, props, replyTo) =>
@ -67,17 +76,3 @@ object SpawnProtocol {
}
}
/**
* A message protocol for actors that support spawning a child actor when receiving a [[SpawnProtocol#Spawn]]
* message and sending back the [[ActorRef]] of the child actor. An implementation of a behavior for this
* protocol is defined in [[SpawnProtocol#behavior]]. That can be used as is or composed with other behavior
* using [[Behavior#orElse]].
*
* The typical usage of this is to use it as the guardian actor of the [[ActorSystem]], possibly combined with
* `Behaviors.setup` to starts some initial tasks or actors. Child actors can then be started from the outside
* by telling or asking [[SpawnProtocol#Spawn]] to the actor reference of the system. When using `ask` this is
* similar to how [[akka.actor.ActorSystem#actorOf]] can be used in untyped actors with the difference that
* a `Future` / `CompletionStage` of the `ActorRef` is returned.
*/
sealed abstract class SpawnProtocol

View file

@ -7,6 +7,7 @@ package akka.cluster.typed.internal
import akka.actor.testkit.typed.scaladsl.TestProbe
import akka.actor.typed.ActorRef
import akka.actor.typed.Behavior
import akka.actor.typed.Props
import akka.actor.typed.SpawnProtocol
import akka.actor.typed.receptionist.Receptionist
import akka.actor.typed.receptionist.ServiceKey
@ -54,11 +55,11 @@ abstract class ClusterReceptionistUnreachabilitySpec
import ClusterReceptionistUnreachabilitySpec._
val spawnActor = system.actorOf(PropsAdapter(SpawnProtocol.behavior)).toTyped[SpawnProtocol]
val spawnActor = system.actorOf(PropsAdapter(SpawnProtocol())).toTyped[SpawnProtocol.Command]
def spawn[T](behavior: Behavior[T], name: String): ActorRef[T] = {
implicit val timeout: Timeout = 3.seconds
implicit val scheduler = typedSystem.scheduler
val f: Future[ActorRef[T]] = spawnActor.ask(ref => SpawnProtocol.Spawn(behavior, name)(ref))
val f: Future[ActorRef[T]] = spawnActor.ask(SpawnProtocol.Spawn(behavior, name, Props.empty, _))
Await.result(f, 3.seconds)
}

View file

@ -462,6 +462,8 @@ made before finalizing the APIs. Compared to Akka 2.5.x the source incompatible
`interceptMessageType` method in `BehaviorInterceptor` is replaced with this @scala[`ClassTag`]@java[`Class`] parameter.
* `Behavior.orElse` has been removed because it wasn't safe together with `narrow`.
* `StashBuffer`s are now created with `Behaviors.withStash` rather than instantiating directly
* To align with the Akka Typed style guide `SpawnProtocol` is now created through @scala[`SpawnProtocol()`]@java[`SpawnProtocol.create()`], the special `Spawn` message
factories has been removed and the top level of the actor protocol is now `SpawnProtocol.Command`
#### Akka Typed Stream API changes