docs: Use apidoc directive in typed/actors.md (#22904) (#31187)

* Use apidoc directive in typed/actors.md (#22904)

* Apply suggestions from code review

fix links

Co-authored-by: Arnout Engelen <arnout@engelen.eu>

Co-authored-by: Arnout Engelen <arnout@engelen.eu>
This commit is contained in:
Andrei Arlou 2022-03-03 16:10:28 +02:00 committed by GitHub
parent 2aaf16c23b
commit b8b821a95a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -74,15 +74,15 @@ Java
This small piece of code defines two message types, one for commanding the This small piece of code defines two message types, one for commanding the
Actor to greet someone and one that the Actor will use to confirm that it has Actor to greet someone and one that the Actor will use to confirm that it has
done so. The `Greet` type contains not only the information of whom to done so. The `Greet` type contains not only the information of whom to
greet, it also holds an `ActorRef` that the sender of the message greet, it also holds an @apidoc[typed.ActorRef] that the sender of the message
supplies so that the `HelloWorld` Actor can send back the confirmation supplies so that the `HelloWorld` Actor can send back the confirmation
message. message.
The behavior of the Actor is defined as the `Greeter` with the help The behavior of the Actor is defined as the `Greeter` with the help
of the `receive` behavior factory. Processing the next message then results of the @apidoc[receive](typed.*.Behaviors$) {scala="#receive[T](onMessage:(akka.actor.typed.scaladsl.ActorContext[T],T)=%3Eakka.actor.typed.Behavior[T]):akka.actor.typed.scaladsl.Behaviors.Receive[T]" java="#receive(akka.japi.function.Function2,akka.japi.function.Function2)"} behavior factory. Processing the next message then results
in a new behavior that can potentially be different from this one. State is in a new behavior that can potentially be different from this one. State is
updated by returning a new behavior that holds the new immutable state. In this updated by returning a new behavior that holds the new immutable state. In this
case we don't need to update any state, so we return @scala[`same`]@java[`this`], which means case we don't need to update any state, so we return @apidoc[same](typed.*.Behaviors$){scala="#same[T]:akka.actor.typed.Behavior[T]" java="#same()"}, which means
the next behavior is "the same as the current one". the next behavior is "the same as the current one".
The type of the messages handled by this behavior is declared to be of class The type of the messages handled by this behavior is declared to be of class
@ -92,7 +92,7 @@ Typically, an actor handles more than one specific message type where all of the
directly or indirectly @scala[`extend`]@java[`implement`] a common @scala[`trait`]@java[`interface`]. directly or indirectly @scala[`extend`]@java[`implement`] a common @scala[`trait`]@java[`interface`].
On the last line we see the `HelloWorld` Actor send a message to another On the last line we see the `HelloWorld` Actor send a message to another
Actor, which is done using the @scala[`!` operator (pronounced “bang” or “tell”)]@java[`tell` method]. Actor, which is done using the @scala[@scaladoc[!](akka.actor.typed.ActorRef#tell(msg:T):Unit) operator (pronounced “bang” or “tell”)]@java[@javadoc[tell](akka.actor.typed.ActorRef#tell(T)) method].
It is an asynchronous operation that doesn't block the caller's thread. It is an asynchronous operation that doesn't block the caller's thread.
Since the `replyTo` address is declared to be of type @scala[`ActorRef[Greeted]`]@java[`ActorRef<Greeted>`], the Since the `replyTo` address is declared to be of type @scala[`ActorRef[Greeted]`]@java[`ActorRef<Greeted>`], the
@ -121,7 +121,7 @@ Java
@scala[Note how this Actor manages the counter by changing the behavior for each `Greeted` reply @scala[Note how this Actor manages the counter by changing the behavior for each `Greeted` reply
rather than using any variables.]@java[Note how this Actor manages the counter with an instance variable.] rather than using any variables.]@java[Note how this Actor manages the counter with an instance variable.]
No concurrency guards such as `synchronized` or `AtomicInteger` are needed since an actor instance processes one No concurrency guards such as `synchronized` or @javadoc[AtomicInteger](java.util.concurrent.atomic.AtomicInteger) are needed since an actor instance processes one
message at a time. message at a time.
A third actor spawns the `Greeter` and the `HelloWorldBot` and starts the interaction between those. A third actor spawns the `Greeter` and the `HelloWorldBot` and starts the interaction between those.
@ -143,7 +143,7 @@ Java
We start an Actor system from the defined `HelloWorldMain` behavior and send two `SayHello` messages that We start an Actor system from the defined `HelloWorldMain` behavior and send two `SayHello` messages that
will kick-off the interaction between two separate `HelloWorldBot` actors and the single `Greeter` actor. will kick-off the interaction between two separate `HelloWorldBot` actors and the single `Greeter` actor.
An application normally consists of a single `ActorSystem`, running many actors, per JVM. An application normally consists of a single @apidoc[typed.ActorSystem], running many actors, per JVM.
The console output may look like this: The console output may look like this:
@ -202,7 +202,7 @@ Scala
Java Java
: @@snip [IntroSpec.scala](/akka-actor-typed-tests/src/test/java/jdocs/akka/typed/IntroTest.java) { #chatroom-protocol } : @@snip [IntroSpec.scala](/akka-actor-typed-tests/src/test/java/jdocs/akka/typed/IntroTest.java) { #chatroom-protocol }
Initially the client Actors only get access to an @scala[`ActorRef[GetSession]`]@java[`ActorRef<GetSession>`] Initially the client Actors only get access to an @apidoc[typed.ActorRef[GetSession]]
which allows them to make the first step. Once a clients session has been which allows them to make the first step. Once a clients session has been
established it gets a `SessionGranted` message that contains a `handle` to established it gets a `SessionGranted` message that contains a `handle` to
unlock the next protocol step, posting messages. The `PostMessage` unlock the next protocol step, posting messages. The `PostMessage`
@ -227,7 +227,7 @@ The state is managed by changing behavior rather than using any variables.
When a new `GetSession` command comes in we add that client to the When a new `GetSession` command comes in we add that client to the
list that is in the returned behavior. Then we also need to create the sessions list that is in the returned behavior. Then we also need to create the sessions
`ActorRef` that will be used to post messages. In this case we want to @apidoc[typed.ActorRef] that will be used to post messages. In this case we want to
create a very simple Actor that repackages the `PostMessage` create a very simple Actor that repackages the `PostMessage`
command into a `PublishSessionMessage` command which also includes the command into a `PublishSessionMessage` command which also includes the
screen name. screen name.
@ -248,7 +248,7 @@ screen name then we could change the protocol such that `PostMessage` is
removed and all clients just get an @scala[`ActorRef[PublishSessionMessage]`]@java[`ActorRef<PublishSessionMessage>`] to removed and all clients just get an @scala[`ActorRef[PublishSessionMessage]`]@java[`ActorRef<PublishSessionMessage>`] to
send to. In this case no session actor would be needed and we could use send to. In this case no session actor would be needed and we could use
@scala[`context.self`]@java[`context.getSelf()`]. The type-checks work out in that case because @scala[`context.self`]@java[`context.getSelf()`]. The type-checks work out in that case because
@scala[`ActorRef[-T]`]@java[`ActorRef<T>`] is contravariant in its type parameter, meaning that we @scala[@apidoc[ActorRef[-T]](typed.ActorRef)]@java[@apidoc[ActorRef<T>](typed.ActorRef)] is contravariant in its type parameter, meaning that we
can use a @scala[`ActorRef[RoomCommand]`]@java[`ActorRef<RoomCommand>`] wherever an can use a @scala[`ActorRef[RoomCommand]`]@java[`ActorRef<RoomCommand>`] wherever an
@scala[`ActorRef[PublishSessionMessage]`]@java[`ActorRef<PublishSessionMessage>`] is needed—this makes sense because the @scala[`ActorRef[PublishSessionMessage]`]@java[`ActorRef<PublishSessionMessage>`] is needed—this makes sense because the
former simply speaks more languages than the latter. The opposite would be former simply speaks more languages than the latter. The opposite would be
@ -269,7 +269,7 @@ From this behavior we can create an Actor that will accept a chat room session,
post a message, wait to see it published, and then terminate. The last step post a message, wait to see it published, and then terminate. The last step
requires the ability to change behavior, we need to transition from the normal requires the ability to change behavior, we need to transition from the normal
running behavior into the terminated state. This is why here we do not return running behavior into the terminated state. This is why here we do not return
`same`, as above, but another special value `stopped`. @apidoc[same](typed.*.Behaviors$){scala="#same[T]:akka.actor.typed.Behavior[T]" java="#same()"}, as above, but another special value @apidoc[stopped](typed.*.Behaviors$){scala="#stopped[T]:akka.actor.typed.Behavior[T]" java="#stopped()"}.
@@@ div {.group-scala} @@@ div {.group-scala}
@ -296,24 +296,24 @@ Java
In good tradition we call the `Main` Actor what it is, it directly In good tradition we call the `Main` Actor what it is, it directly
corresponds to the `main` method in a traditional Java application. This corresponds to the `main` method in a traditional Java application. This
Actor will perform its job on its own accord, we do not need to send messages Actor will perform its job on its own accord, we do not need to send messages
from the outside, so we declare it to be of type @scala[`NotUsed`]@java[`Void`]. Actors receive not from the outside, so we declare it to be of type @scala[@scaladoc[NotUsed](akka.NotUsed)]@java[@javadoc[Void](java.lang.Void)]. Actors receive not
only external messages, they also are notified of certain system events, only external messages, they also are notified of certain system events,
so-called Signals. In order to get access to those we choose to implement this so-called Signals. In order to get access to those we choose to implement this
particular one using the `receive` behavior decorator. The particular one using the @apidoc[receive](typed.*.Behaviors$) {scala="#receive[T](onMessage:(akka.actor.typed.scaladsl.ActorContext[T],T)=%3Eakka.actor.typed.Behavior[T]):akka.actor.typed.scaladsl.Behaviors.Receive[T]" java="#receive(akka.japi.function.Function2)"} behavior decorator. The
provided `onSignal` function will be invoked for signals (subclasses of `Signal`) provided `onSignal` function will be invoked for signals (subclasses of @apidoc[typed.Signal])
or the `onMessage` function for user messages. or the `onMessage` function for user messages.
This particular `Main` Actor is created using `Behaviors.setup`, which is like a factory for a behavior. This particular `Main` Actor is created using @apidoc[Behaviors.setup](typed.*.Behaviors$) {scala="#setup[T](factory:akka.actor.typed.scaladsl.ActorContext[T]=%3Eakka.actor.typed.Behavior[T]):akka.actor.typed.Behavior[T]" java="#setup(akka.japi.function.Function)"}, which is like a factory for a behavior.
Creation of the behavior instance is deferred until the actor is started, as opposed to `Behaviors.receive` Creation of the behavior instance is deferred until the actor is started, as opposed to @apidoc[Behaviors.receive](typed.*.Behaviors$) {scala="#receive[T](onMessage:(akka.actor.typed.scaladsl.ActorContext[T],T)=%3Eakka.actor.typed.Behavior[T]):akka.actor.typed.scaladsl.Behaviors.Receive[T]" java="#receive(akka.japi.function.Function2)"}
that creates the behavior instance immediately before the actor is running. The factory function in that creates the behavior instance immediately before the actor is running. The factory function in
`setup` is passed the `ActorContext` as parameter and that can for example be used for spawning child actors. `setup` is passed the @apidoc[typed.*.ActorContext] as parameter and that can for example be used for spawning child actors.
This `Main` Actor creates the chat room and the gabbler and the session between them is initiated, and when the This `Main` Actor creates the chat room and the gabbler and the session between them is initiated, and when the
gabbler is finished we will receive the `Terminated` event due to having gabbler is finished we will receive the @apidoc[typed.Terminated] event due to having
called `context.watch` for it. This allows us to shut down the Actor system: when called @apidoc[context.watch](typed.*.ActorContext) {scala="#watch[U](other:akka.actor.typed.ActorRef[U]):Unit" java="#watch(akka.actor.typed.ActorRef)"} for it. This allows us to shut down the Actor system: when
the `Main` Actor terminates there is nothing more to do. the `Main` Actor terminates there is nothing more to do.
Therefore after creating the Actor system with the `Main` Actors Therefore after creating the Actor system with the `Main` Actors
`Behavior` we can let the `main` method return, the `ActorSystem` will continue running and @apidoc[typed.Behavior] we can let the `main` method return, the @apidoc[typed.ActorSystem] will continue running and
the JVM alive until the root actor stops. the JVM alive until the root actor stops.
@ -332,8 +332,7 @@ is best for a specific actor. Considerations for the choice is provided in the
#### AbstractBehavior API #### AbstractBehavior API
Defining a class based actor behavior starts with extending Defining a class based actor behavior starts with extending
@scala[`akka.actor.typed.scaladsl.AbstractBehavior[T]`] @apidoc[akka.actor.typed.*.AbstractBehavior]@java[`<T>`]@scala[`[T]`] where `T` is the type of messages
@java[`akka.actor.typed.javadsl.AbstractBehavior<T>`] where `T` is the type of messages
the behavior will accept. the behavior will accept.
Let's repeat the chat room sample from @ref:[A more complex example above](#a-more-complex-example) but implemented Let's repeat the chat room sample from @ref:[A more complex example above](#a-more-complex-example) but implemented
@ -368,7 +367,7 @@ Java
The state is managed through fields in the class, just like with a regular object oriented class. The state is managed through fields in the class, just like with a regular object oriented class.
As the state is mutable, we never return a different behavior from the message logic, but can return As the state is mutable, we never return a different behavior from the message logic, but can return
the `AbstractBehavior` instance itself (`this`) as a behavior to use for processing the next message coming in. the `AbstractBehavior` instance itself (`this`) as a behavior to use for processing the next message coming in.
We could also return `Behavior.same` to achieve the same. We could also return @apidoc[Behaviors.same](typed.*.Behaviors$) {scala="#same[T]:akka.actor.typed.Behavior[T]" java="#same()"} to achieve the same.
@java[In this sample we make separate statements for creating the behavior builder, but it also returns the builder @java[In this sample we make separate statements for creating the behavior builder, but it also returns the builder
itself from each step so a more fluent behavior definition style is also possible. What you should prefer depends on itself from each step so a more fluent behavior definition style is also possible. What you should prefer depends on
@ -380,14 +379,14 @@ with the functional style for different parts of the lifecycle of the same Actor
When a new `GetSession` command comes in we add that client to the When a new `GetSession` command comes in we add that client to the
list of current sessions. Then we also need to create the sessions list of current sessions. Then we also need to create the sessions
`ActorRef` that will be used to post messages. In this case we want to @apidoc[typed.ActorRef] that will be used to post messages. In this case we want to
create a very simple Actor that repackages the `PostMessage` create a very simple Actor that repackages the `PostMessage`
command into a `PublishSessionMessage` command which also includes the command into a `PublishSessionMessage` command which also includes the
screen name. screen name.
To implement the logic where we spawn a child for the session we need access To implement the logic where we spawn a child for the session we need access
to the `ActorContext`. This is injected as a constructor parameter upon creation to the @apidoc[typed.*.ActorContext]. This is injected as a constructor parameter upon creation
of the behavior, note how we combine the `AbstractBehavior` with `Behaviors.setup` of the behavior, note how we combine the @apidoc[typed.*.AbstractBehavior] with @apidoc[Behaviors.setup](typed.*.Behaviors$) {scala="#setup[T](factory:akka.actor.typed.scaladsl.ActorContext[T]=%3Eakka.actor.typed.Behavior[T]):akka.actor.typed.Behavior[T]" java="#setup(akka.japi.function.Function)"}
to do this in the @scala[`apply`]@java[`create`] factory method. to do this in the @scala[`apply`]@java[`create`] factory method.
The behavior that we declare here can handle both subtypes of `RoomCommand`. The behavior that we declare here can handle both subtypes of `RoomCommand`.
@ -405,7 +404,7 @@ If we did not care about securing the correspondence between a session and a
screen name then we could change the protocol such that `PostMessage` is screen name then we could change the protocol such that `PostMessage` is
removed and all clients just get an @scala[`ActorRef[PublishSessionMessage]`]@java[`ActorRef<PublishSessionMessage>`] to removed and all clients just get an @scala[`ActorRef[PublishSessionMessage]`]@java[`ActorRef<PublishSessionMessage>`] to
send to. In this case no session actor would be needed and we could use send to. In this case no session actor would be needed and we could use
@scala[`context.self`]@java[`context.getSelf()`]. The type-checks work out in that case because @scala[@scaladoc[context.self](akka.actor.typed.scaladsl.ActorContext#self:akka.actor.typed.ActorRef[T])]@java[@javadoc[context.getSelf()](akka.actor.typed.javadsl.ActorContext#getSelf())]. The type-checks work out in that case because
@scala[`ActorRef[-T]`]@java[`ActorRef<T>`] is contravariant in its type parameter, meaning that we @scala[`ActorRef[-T]`]@java[`ActorRef<T>`] is contravariant in its type parameter, meaning that we
can use a @scala[`ActorRef[RoomCommand]`]@java[`ActorRef<RoomCommand>`] wherever an can use a @scala[`ActorRef[RoomCommand]`]@java[`ActorRef<RoomCommand>`] wherever an
@scala[`ActorRef[PublishSessionMessage]`]@java[`ActorRef<PublishSessionMessage>`] is needed—this makes sense because the @scala[`ActorRef[PublishSessionMessage]`]@java[`ActorRef<PublishSessionMessage>`] is needed—this makes sense because the
@ -441,22 +440,22 @@ Java
In good tradition we call the `Main` Actor what it is, it directly In good tradition we call the `Main` Actor what it is, it directly
corresponds to the `main` method in a traditional Java application. This corresponds to the `main` method in a traditional Java application. This
Actor will perform its job on its own accord, we do not need to send messages Actor will perform its job on its own accord, we do not need to send messages
from the outside, so we declare it to be of type @scala[`NotUsed`]@java[`Void`]. Actors receive not from the outside, so we declare it to be of type @scala[@scaladoc[NotUsed](akka.NotUsed)]@java[@javadoc[Void](java.lang.Void)]. Actors receive not
only external messages, they also are notified of certain system events, only external messages, they also are notified of certain system events,
so-called Signals. In order to get access to those we choose to implement this so-called Signals. In order to get access to those we choose to implement this
particular one using the `receive` behavior decorator. The particular one using the @apidoc[receive](typed.*.Behaviors$) {scala="#receive[T](onMessage:(akka.actor.typed.scaladsl.ActorContext[T],T)=%3Eakka.actor.typed.Behavior[T]):akka.actor.typed.scaladsl.Behaviors.Receive[T]" java="#receive(akka.japi.function.Function2)"} behavior decorator. The
provided `onSignal` function will be invoked for signals (subclasses of `Signal`) provided `onSignal` function will be invoked for signals (subclasses of @apidoc[typed.Signal])
or the `onMessage` function for user messages. or the `onMessage` function for user messages.
This particular `Main` Actor is created using `Behaviors.setup`, which is like a factory for a behavior. This particular `Main` Actor is created using @apidoc[Behaviors.setup](typed.*.Behaviors$) {scala="#setup[T](factory:akka.actor.typed.scaladsl.ActorContext[T]=%3Eakka.actor.typed.Behavior[T]):akka.actor.typed.Behavior[T]" java="#setup(akka.japi.function.Function)"}, which is like a factory for a behavior.
Creation of the behavior instance is deferred until the actor is started, as opposed to `Behaviors.receive` Creation of the behavior instance is deferred until the actor is started, as opposed to @apidoc[Behaviors.receive](typed.*.Behaviors$) {scala="#receive[T](onMessage:(akka.actor.typed.scaladsl.ActorContext[T],T)=%3Eakka.actor.typed.Behavior[T]):akka.actor.typed.scaladsl.Behaviors.Receive[T]" java="#receive(akka.japi.function.Function2)"}
that creates the behavior instance immediately before the actor is running. The factory function in that creates the behavior instance immediately before the actor is running. The factory function in
`setup` is passed the `ActorContext` as parameter and that can for example be used for spawning child actors. `setup` is passed the @apidoc[typed.*.ActorContext] as parameter and that can for example be used for spawning child actors.
This `Main` Actor creates the chat room and the gabbler and the session between them is initiated, and when the This `Main` Actor creates the chat room and the gabbler and the session between them is initiated, and when the
gabbler is finished we will receive the `Terminated` event due to having gabbler is finished we will receive the @apidoc[typed.Terminated] event due to having
called `context.watch` for it. This allows us to shut down the Actor system: when called @apidoc[context.watch](typed.*.ActorContext) {scala="#watch[U](other:akka.actor.typed.ActorRef[U]):Unit" java="#watch(akka.actor.typed.ActorRef)"} for it. This allows us to shut down the Actor system: when
the `Main` Actor terminates there is nothing more to do. the `Main` Actor terminates there is nothing more to do.
Therefore after creating the Actor system with the `Main` Actors Therefore after creating the Actor system with the `Main` Actors
`Behavior` we can let the `main` method return, the `ActorSystem` will continue running and @apidoc[typed.Behavior] we can let the `main` method return, the @apidoc[typed.ActorSystem] will continue running and
the JVM alive until the root actor stops. the JVM alive until the root actor stops.