diff --git a/akka-actor-typed-tests/src/test/java/jdocs/akka/typed/StyleGuideDocExamples.java b/akka-actor-typed-tests/src/test/java/jdocs/akka/typed/StyleGuideDocExamples.java index 1e862fe54e..77cc7a24cd 100644 --- a/akka-actor-typed-tests/src/test/java/jdocs/akka/typed/StyleGuideDocExamples.java +++ b/akka-actor-typed-tests/src/test/java/jdocs/akka/typed/StyleGuideDocExamples.java @@ -7,6 +7,7 @@ package jdocs.akka.typed; // #oo-style // #fun-style import akka.actor.typed.Behavior; +import akka.actor.typed.SupervisorStrategy; import akka.actor.typed.javadsl.ActorContext; import akka.actor.typed.javadsl.Behaviors; // #fun-style @@ -732,4 +733,55 @@ interface StyleGuideDocExamples { } // #public-private-messages-2 } + + interface NestingSample1 { + interface Command {} + + // #nesting + public static Behavior apply() { + return Behaviors.setup( + context -> + Behaviors.withStash( + 100, + stash -> + Behaviors.withTimers( + timers -> { + context.getLog().debug("Starting up"); + + // behavior using context, stash and timers ... + // #nesting + return Behaviors.empty(); + // #nesting + }))); + } + // #nesting + } + + interface NestingSample2 { + interface Command {} + + // #nesting-supervise + public static Behavior create() { + return Behaviors.setup( + context -> { + // only run on initial actor start, not on crash-restart + context.getLog().info("Starting"); + + return Behaviors.supervise( + Behaviors.withStash( + 100, + stash -> { + // every time the actor crashes and restarts a new stash is created + // (previous stash is lost) + context.getLog().debug("Starting up with stash"); + // Behaviors.receiveMessage { ... } + // #nesting-supervise + return Behaviors.empty(); + // #nesting-supervise + })) + .onFailure(RuntimeException.class, SupervisorStrategy.restart()); + }); + } + // #nesting-supervise + } } diff --git a/akka-actor-typed-tests/src/test/scala/docs/akka/typed/StyleGuideDocExamples.scala b/akka-actor-typed-tests/src/test/scala/docs/akka/typed/StyleGuideDocExamples.scala index c9164303cc..3ceb783724 100644 --- a/akka-actor-typed-tests/src/test/scala/docs/akka/typed/StyleGuideDocExamples.scala +++ b/akka-actor-typed-tests/src/test/scala/docs/akka/typed/StyleGuideDocExamples.scala @@ -6,13 +6,13 @@ package docs.akka.typed import scala.concurrent.duration._ import scala.concurrent.Future - import akka.actor.typed.ActorSystem import akka.actor.typed.ActorRef import akka.actor.typed.scaladsl.LoggerOps import akka.actor.typed.scaladsl.TimerScheduler -import scala.concurrent.duration.FiniteDuration +import akka.actor.typed.SupervisorStrategy +import scala.concurrent.duration.FiniteDuration import akka.Done import com.github.ghik.silencer.silent @@ -521,4 +521,48 @@ object StyleGuideDocExamples { } } + + object NestingSample1 { + sealed trait Command + + //#nesting + def apply(): Behavior[Command] = + Behaviors.setup[Command](context => + Behaviors.withStash(100)(stash => + Behaviors.withTimers { timers => + context.log.debug("Starting up") + + // behavior using context, stash and timers ... + //#nesting + timers.isTimerActive("aa") + stash.isEmpty + Behaviors.empty + //#nesting + })) + //#nesting + } + + object NestingSample2 { + sealed trait Command + + //#nesting-supervise + def apply(): Behavior[Command] = + Behaviors.setup { context => + // only run on initial actor start, not on crash-restart + context.log.info("Starting") + + Behaviors + .supervise(Behaviors.withStash[Command](100) { stash => + // every time the actor crashes and restarts a new stash is created (previous stash is lost) + context.log.debug("Starting up with stash") + // Behaviors.receiveMessage { ... } + //#nesting-supervise + stash.isEmpty + Behaviors.empty + //#nesting-supervise + }) + .onFailure[RuntimeException](SupervisorStrategy.restart) + } + //#nesting-supervise + } } diff --git a/akka-docs/src/main/paradox/typed/style-guide.md b/akka-docs/src/main/paradox/typed/style-guide.md index 13c9068569..49a600268c 100644 --- a/akka-docs/src/main/paradox/typed/style-guide.md +++ b/akka-docs/src/main/paradox/typed/style-guide.md @@ -457,6 +457,29 @@ of using `newReceiveBuilder`. Implement the `receiveMessage` and `receiveSignal` @@@ +## Nesting setup + +When an actor behavior needs more than one of `setup`, `withTimers` and `withStash` the methods can be nested to access +the needed dependencies: + +Scala +: @@snip [StyleGuideDocExamples.scala](/akka-actor-typed-tests/src/test/scala/docs/akka/typed/StyleGuideDocExamples.scala) { #nesting } + +Java +: @@snip [StyleGuideDocExamples.java](/akka-actor-typed-tests/src/test/java/jdocs/akka/typed/StyleGuideDocExamples.java) { #nesting } + +The order of the nesting does not change the behavior as long as there is no additional logic in any other function than the innermost one. +It can be nice to default to put `setup` outermost as that is the least likely block that will be removed if the actor logic changes. + +Note that adding `supervise` to the mix is different as it will restart the behavior it wraps, but not the behavior around itself: + +Scala +: @@snip [StyleGuideDocExamples.scala](/akka-actor-typed-tests/src/test/scala/docs/akka/typed/StyleGuideDocExamples.scala) { #nesting-supervise } + +Java +: @@snip [StyleGuideDocExamples.java](/akka-actor-typed-tests/src/test/java/jdocs/akka/typed/StyleGuideDocExamples.java) { #nesting-supervise } + + ## Additional naming conventions Some naming conventions have already been mentioned in the context of other recommendations, but here