diff --git a/akka-actor-typed-tests/src/test/java/akka/actor/typed/javadsl/ActorCompile.java b/akka-actor-typed-tests/src/test/java/akka/actor/typed/javadsl/ActorCompile.java index 344695360e..e045bb5829 100644 --- a/akka-actor-typed-tests/src/test/java/akka/actor/typed/javadsl/ActorCompile.java +++ b/akka-actor-typed-tests/src/test/java/akka/actor/typed/javadsl/ActorCompile.java @@ -7,6 +7,8 @@ package akka.actor.typed.javadsl; import akka.actor.typed.*; import akka.actor.typed.TypedActorContext; +import java.math.BigDecimal; +import java.math.BigInteger; import java.time.Duration; import java.util.concurrent.CompletionStage; @@ -62,7 +64,28 @@ public class ActorCompile { final ActorRef self = context.getSelf(); return monitor(MyMsg.class, self, ignore()); }); - Behavior actor9 = widened(MyMsgA.class, actor7, pf -> pf.match(MyMsgA.class, x -> x)); + + Behavior actor9a = + transformMessages(MyMsgA.class, actor7, pf -> pf.match(MyMsgA.class, x -> x)); + Behavior actor9b = + transformMessages(MyMsg.class, actor7, pf -> pf.match(MyMsgA.class, x -> x)); + + // this is the example from the Javadoc + Behavior s = + Behaviors.receive( + (ctx, msg) -> { + return Behaviors.same(); + }); + Behavior n = + Behaviors.transformMessages( + Number.class, + s, + pf -> + pf.match(BigInteger.class, i -> "BigInteger(" + i + ")") + .match(BigDecimal.class, d -> "BigDecimal(" + d + ")") + // drop all other kinds of Number + ); + Behavior actor10 = Behaviors.receive((context, message) -> stopped(() -> {}), (context, signal) -> same()); diff --git a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/ActorContextSpec.scala b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/ActorContextSpec.scala index d58bbc02e1..37ddf0aeb7 100644 --- a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/ActorContextSpec.scala +++ b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/ActorContextSpec.scala @@ -683,9 +683,9 @@ class NormalActorContextSpec extends ActorContextSpec { override def decoration[T: ClassTag] = x => x } -class WidenActorContextSpec extends ActorContextSpec { +class TransformMessagesActorContextSpec extends ActorContextSpec { - override def decoration[T: ClassTag] = b => b.widen { case x => x } + override def decoration[T: ClassTag] = b => b.transformMessages { case x => x } } class DeferredActorContextSpec extends ActorContextSpec { diff --git a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/BehaviorSpec.scala b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/BehaviorSpec.scala index d083969ac4..d1d6eaad18 100644 --- a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/BehaviorSpec.scala +++ b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/BehaviorSpec.scala @@ -469,11 +469,11 @@ class MutableScalaBehaviorSpec extends Messages with Become with Stoppable { } } -class WidenedScalaBehaviorSpec extends ImmutableWithSignalScalaBehaviorSpec with Reuse with Siphon { +class TransformMessagesScalaBehaviorSpec extends ImmutableWithSignalScalaBehaviorSpec with Reuse with Siphon { override def behavior(monitor: ActorRef[Event]): (Behavior[Command], Aux) = { - val inbox = TestInbox[Command]("widenedListener") - super.behavior(monitor)._1.widen[Command] { case c => inbox.ref ! c; c } -> inbox + val inbox = TestInbox[Command]("transformMessagesListener") + super.behavior(monitor)._1.transformMessages[Command] { case c => inbox.ref ! c; c } -> inbox } } @@ -586,10 +586,10 @@ class ImmutableJavaBehaviorSpec extends Messages with Become with Stoppable { } } -class WidenedJavaBehaviorSpec extends ImmutableWithSignalJavaBehaviorSpec with Reuse with Siphon { +class TransformMessagesJavaBehaviorSpec extends ImmutableWithSignalJavaBehaviorSpec with Reuse with Siphon { override def behavior(monitor: ActorRef[Event]): (Behavior[Command], Aux) = { - val inbox = TestInbox[Command]("widenedListener") - JBehaviors.widened(classOf[Command], super.behavior(monitor)._1, pf(_.`match`(classOf[Command], fi(x => { + val inbox = TestInbox[Command]("transformMessagesListener") + JBehaviors.transformMessages(classOf[Command], super.behavior(monitor)._1, pf(_.`match`(classOf[Command], fi(x => { inbox.ref ! x x })))) -> inbox diff --git a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/DeferredSpec.scala b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/DeferredSpec.scala index 61c04621fd..bdccbb8681 100644 --- a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/DeferredSpec.scala +++ b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/DeferredSpec.scala @@ -102,14 +102,14 @@ class DeferredSpec extends ScalaTestWithActorTestKit(""" probe.expectMessage(Started) } - "must un-defer underlying when wrapped by widen" in { + "must un-defer underlying when wrapped by transformMessages" in { val probe = TestProbe[Event]("evt") val behv = Behaviors .setup[Command] { _ => probe.ref ! Started target(probe.ref) } - .widen[Command] { + .transformMessages[Command] { case m => m } probe.expectNoMessage() // not yet diff --git a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/InterceptSpec.scala b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/InterceptSpec.scala index dea99b7b0d..dac6194ae2 100644 --- a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/InterceptSpec.scala +++ b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/InterceptSpec.scala @@ -470,16 +470,16 @@ class InterceptSpec extends ScalaTestWithActorTestKit(""" probe.expectMessage("b") // bypass toUpper interceptor } - "be possible to combine with widen" in { + "be possible to combine with transformMessages" in { val probe = createTestProbe[String]() - val ref = spawn(MultiProtocol(probe.ref).widen[String] { + val ref = spawn(MultiProtocol(probe.ref).transformMessages[String] { case s => Command(s.toUpperCase()) }) ref ! "a" probe.expectMessage("A") ref.unsafeUpcast ! ExternalResponse("b") - probe.expectMessage("b") // bypass widen interceptor + probe.expectMessage("b") // bypass transformMessages interceptor } "be possible to combine with MDC" in { diff --git a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/WidenSpec.scala b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/TransformMessagesSpec.scala similarity index 69% rename from akka-actor-typed-tests/src/test/scala/akka/actor/typed/WidenSpec.scala rename to akka-actor-typed-tests/src/test/scala/akka/actor/typed/TransformMessagesSpec.scala index 4f6f17e409..6cd24e5c0a 100644 --- a/akka-actor-typed-tests/src/test/scala/akka/actor/typed/WidenSpec.scala +++ b/akka-actor-typed-tests/src/test/scala/akka/actor/typed/TransformMessagesSpec.scala @@ -4,6 +4,7 @@ package akka.actor.typed +import java.math.BigInteger import java.util.concurrent.atomic.AtomicInteger import akka.actor.ActorInitializationException @@ -13,10 +14,25 @@ import akka.actor.typed.scaladsl.Behaviors import akka.actor.typed.scaladsl.adapter._ import akka.testkit.EventFilter import org.scalatest.WordSpecLike - import scala.concurrent.duration._ -class WidenSpec extends ScalaTestWithActorTestKit(""" +object TransformMessagesSpec { + + // this is the sample from the Scaladoc + val b: Behavior[Number] = + Behaviors + .receive[String] { (ctx, msg) => + println(msg) + Behaviors.same + } + .transformMessages[Number] { + case b: BigDecimal => s"BigDecimal($b)" + case i: BigInt => s"BigInteger($i)" + // all other kinds of Number will be `unhandled` + } +} + +class TransformMessagesSpec extends ScalaTestWithActorTestKit(""" akka.loggers = [akka.testkit.TestEventListener] """) with WordSpecLike { @@ -28,12 +44,12 @@ class WidenSpec extends ScalaTestWithActorTestKit(""" probe ! message Behaviors.same } - .widen[Int] { + .transformMessages[Int] { case n if n != 13 => n.toString } } - "Widen" should { + "transformMessages" should { "transform from an outer type to an inner type" in { val probe = TestProbe[String]() @@ -57,21 +73,21 @@ class WidenSpec extends ScalaTestWithActorTestKit(""" } } - "not build up when the same widen is used many times (initially)" in { + "not build up when the same transformMessages is used many times (initially)" in { val probe = TestProbe[String]() val transformCount = new AtomicInteger(0) // sadly the only "same" we can know is if it is the same PF - val transform: PartialFunction[String, String] = { + val transformPF: PartialFunction[String, String] = { case s => transformCount.incrementAndGet() s } - def widen(behavior: Behavior[String]): Behavior[String] = - behavior.widen(transform) + def transform(behavior: Behavior[String]): Behavior[String] = + behavior.transformMessages(transformPF) val beh = - widen(widen(Behaviors.receiveMessage[String] { message => + transform(transform(Behaviors.receiveMessage[String] { message => probe.ref ! message Behaviors.same })) @@ -83,21 +99,21 @@ class WidenSpec extends ScalaTestWithActorTestKit(""" transformCount.get should ===(1) } - "not build up when the same widen is used many times (recursively)" in { + "not build up when the same transformMessages is used many times (recursively)" in { val probe = TestProbe[String]() val transformCount = new AtomicInteger(0) // sadly the only "same" we can know is if it is the same PF - val transform: PartialFunction[String, String] = { + val transformPF: PartialFunction[String, String] = { case s => transformCount.incrementAndGet() s } - def widen(behavior: Behavior[String]): Behavior[String] = - behavior.widen(transform) + def transform(behavior: Behavior[String]): Behavior[String] = + behavior.transformMessages(transformPF) def next: Behavior[String] = - widen(Behaviors.receiveMessage[String] { message => + transform(Behaviors.receiveMessage[String] { message => probe.ref ! message next }) @@ -114,16 +130,16 @@ class WidenSpec extends ScalaTestWithActorTestKit(""" } - "not allow mixing different widens in the same behavior stack" in { + "not allow mixing different transformMessages in the same behavior stack" in { val probe = TestProbe[String]() - def widen(behavior: Behavior[String]): Behavior[String] = - behavior.widen[String] { + def transform(behavior: Behavior[String]): Behavior[String] = + behavior.transformMessages[String] { case s => s.toLowerCase } EventFilter[ActorInitializationException](occurrences = 1).intercept { - val ref = spawn(widen(widen(Behaviors.receiveMessage[String] { _ => + val ref = spawn(transform(transform(Behaviors.receiveMessage[String] { _ => Behaviors.same }))) @@ -142,7 +158,7 @@ class WidenSpec extends ScalaTestWithActorTestKit(""" Behaviors.same } } - .widen[String] { + .transformMessages[String] { case msg => msg.toUpperCase() } @@ -163,7 +179,7 @@ class WidenSpec extends ScalaTestWithActorTestKit(""" probe.ref ! msg Behaviors.same } - .widen[String] { + .transformMessages[String] { case msg => msg.toUpperCase() } } diff --git a/akka-actor-typed/src/main/scala/akka/actor/typed/Behavior.scala b/akka-actor-typed/src/main/scala/akka/actor/typed/Behavior.scala index 3e44542bfb..34d8860b6b 100644 --- a/akka-actor-typed/src/main/scala/akka/actor/typed/Behavior.scala +++ b/akka-actor-typed/src/main/scala/akka/actor/typed/Behavior.scala @@ -110,18 +110,24 @@ object Behavior { final implicit class BehaviorDecorators[Inner](val behavior: Behavior[Inner]) extends AnyVal { /** - * Widen the wrapped Behavior by placing a funnel in front of it: the supplied + * Transform the incoming messages by placing a funnel in front of the wrapped `Behavior`: the supplied * PartialFunction decides which message to pull in (those that it is defined * at) and may transform the incoming message to place them into the wrapped * Behavior’s type hierarchy. Signals are not transformed. * * Example: * {{{ - * receive[String] { (ctx, msg) => println(msg); same }.widen[Number] { - * case b: BigDecimal => s"BigDecimal($b)" - * case i: BigInteger => s"BigInteger($i)" - * // all other kinds of Number will be `unhandled` - * } + * val b: Behavior[Number] = + * Behaviors + * .receive[String] { (ctx, msg) => + * println(msg) + * Behaviors.same + * } + * .transformMessages[Number] { + * case b: BigDecimal => s"BigDecimal($b)" + * case i: BigInt => s"BigInteger($i)" + * // all other kinds of Number will be `unhandled` + * } * }}} * * The `ClassTag` for `Outer` ensures that only messages of this class or a subclass thereof will be @@ -129,8 +135,8 @@ object Behavior { * the interceptor and be continue to the inner behavior untouched. * */ - def widen[Outer: ClassTag](matcher: PartialFunction[Outer, Inner]): Behavior[Outer] = - BehaviorImpl.widened(behavior, matcher) + def transformMessages[Outer: ClassTag](matcher: PartialFunction[Outer, Inner]): Behavior[Outer] = + BehaviorImpl.transformMessages(behavior, matcher) } diff --git a/akka-actor-typed/src/main/scala/akka/actor/typed/internal/BehaviorImpl.scala b/akka-actor-typed/src/main/scala/akka/actor/typed/internal/BehaviorImpl.scala index 581b5ea7b9..a3d6cb9a05 100644 --- a/akka-actor-typed/src/main/scala/akka/actor/typed/internal/BehaviorImpl.scala +++ b/akka-actor-typed/src/main/scala/akka/actor/typed/internal/BehaviorImpl.scala @@ -42,8 +42,8 @@ private[akka] object BehaviorTags { def as[U]: AC[U] = ctx.asInstanceOf[AC[U]] } - def widened[O: ClassTag, I](behavior: Behavior[I], matcher: PartialFunction[O, I]): Behavior[O] = - intercept(() => WidenedInterceptor(matcher))(behavior) + def transformMessages[O: ClassTag, I](behavior: Behavior[I], matcher: PartialFunction[O, I]): Behavior[O] = + intercept(() => TransformMessagesInterceptor(matcher))(behavior) def same[T]: Behavior[T] = SameBehavior.unsafeCast[T] diff --git a/akka-actor-typed/src/main/scala/akka/actor/typed/internal/InterceptorImpl.scala b/akka-actor-typed/src/main/scala/akka/actor/typed/internal/InterceptorImpl.scala index 8bb0934c97..87d1950d24 100644 --- a/akka-actor-typed/src/main/scala/akka/actor/typed/internal/InterceptorImpl.scala +++ b/akka-actor-typed/src/main/scala/akka/actor/typed/internal/InterceptorImpl.scala @@ -177,7 +177,7 @@ private[akka] final class LogMessagesInterceptor(val opts: LogOptions) extends B * INTERNAL API */ @InternalApi -private[akka] object WidenedInterceptor { +private[akka] object TransformMessagesInterceptor { private final val _any2null = (_: Any) => null private final def any2null[T] = _any2null.asInstanceOf[Any => T] @@ -187,19 +187,19 @@ private[akka] object WidenedInterceptor { * INTERNAL API */ @InternalApi -private[akka] final case class WidenedInterceptor[O: ClassTag, I](matcher: PartialFunction[O, I]) +private[akka] final case class TransformMessagesInterceptor[O: ClassTag, I](matcher: PartialFunction[O, I]) extends BehaviorInterceptor[O, I] { import BehaviorInterceptor._ - import WidenedInterceptor._ + import TransformMessagesInterceptor._ override def isSame(other: BehaviorInterceptor[Any, Any]): Boolean = other match { // If they use the same pf instance we can allow it, to have one way to workaround defining // "recursive" narrowed behaviors. - case WidenedInterceptor(`matcher`) => true - case WidenedInterceptor(otherMatcher) => + case TransformMessagesInterceptor(`matcher`) => true + case TransformMessagesInterceptor(otherMatcher) => // there is no safe way to allow this throw new IllegalStateException( - "Widen can only be used one time in the same behavior stack. " + + "transformMessages can only be used one time in the same behavior stack. " + s"One defined in ${LineNumbers(matcher)}, and another in ${LineNumbers(otherMatcher)}") case _ => false } @@ -211,5 +211,5 @@ private[akka] final case class WidenedInterceptor[O: ClassTag, I](matcher: Parti } } - override def toString: String = s"Widen(${LineNumbers(matcher)})" + override def toString: String = s"TransformMessages(${LineNumbers(matcher)})" } diff --git a/akka-actor-typed/src/main/scala/akka/actor/typed/javadsl/Behaviors.scala b/akka-actor-typed/src/main/scala/akka/actor/typed/javadsl/Behaviors.scala index 91570f7548..eba5af2fae 100644 --- a/akka-actor-typed/src/main/scala/akka/actor/typed/javadsl/Behaviors.scala +++ b/akka-actor-typed/src/main/scala/akka/actor/typed/javadsl/Behaviors.scala @@ -270,25 +270,24 @@ object Behaviors { } /** - * Widen the wrapped Behavior by placing a funnel in front of it: the supplied + * Transform the incoming messages by placing a funnel in front of the wrapped `Behavior`: the supplied * PartialFunction decides which message to pull in (those that it is defined * at) and may transform the incoming message to place them into the wrapped * Behavior’s type hierarchy. Signals are not transformed. * * Example: * {{{ - * Behavior s = Behaviors.receive((ctx, msg) -> { - * System.out.println(msg); - * return Behaviors.same(); - * }); - * Behavior n = Behaviors.widened(s, pf -> pf. - * match(BigInteger.class, i -> "BigInteger(" + i + ")"). - * match(BigDecimal.class, d -> "BigDecimal(" + d + ")") + * Behavior s = Behaviors.receive((ctx, msg) -> { + * return Behaviors.same(); + * }); + * Behavior n = Behaviors.transformMessages(Number.class, s, pf -> + * pf + * .match(BigInteger.class, i -> "BigInteger(" + i + ")") + * .match(BigDecimal.class, d -> "BigDecimal(" + d + ")") * // drop all other kinds of Number - * ); + * ); * }}} * - * * @param interceptMessageClass Ensures that only messages of this class or a subclass thereof will be * intercepted. Other message types (e.g. a private protocol) will bypass * the interceptor and be continue to the inner behavior untouched. @@ -297,13 +296,13 @@ object Behaviors { * @param selector * a partial function builder for describing the selection and * transformation - * @return a behavior of the widened type + * @return a behavior of the `Outer` type */ - def widened[Outer, Inner]( + def transformMessages[Outer, Inner]( interceptMessageClass: Class[Outer], - behavior: Behavior[Outer], - selector: JFunction[PFBuilder[Inner, Outer], PFBuilder[Inner, Outer]]): Behavior[Inner] = - BehaviorImpl.widened(behavior, selector.apply(new PFBuilder).build())(ClassTag(interceptMessageClass)) + behavior: Behavior[Inner], + selector: JFunction[PFBuilder[Outer, Inner], PFBuilder[Outer, Inner]]): Behavior[Outer] = + BehaviorImpl.transformMessages(behavior, selector.apply(new PFBuilder).build())(ClassTag(interceptMessageClass)) /** * Support for scheduled `self` messages in an actor. diff --git a/akka-docs/src/main/paradox/project/migration-guide-2.5.x-2.6.x.md b/akka-docs/src/main/paradox/project/migration-guide-2.5.x-2.6.x.md index 6bd2ea2b14..315ed02daf 100644 --- a/akka-docs/src/main/paradox/project/migration-guide-2.5.x-2.6.x.md +++ b/akka-docs/src/main/paradox/project/migration-guide-2.5.x-2.6.x.md @@ -393,7 +393,8 @@ made before finalizing the APIs. Compared to Akka 2.5.x the source incompatible to retrieve `ActorContext`, and use an enclosing class to hold initialization parameters and `ActorContext`. * Java @javadoc[EntityRef](akka.cluster.sharding.typed.javadsl.EntityRef) ask timeout now takes a `java.time.Duration` rather than a @apidoc[Timeout] * Changed method signature for `EventAdapter.fromJournal` and support for `manifest` in `EventAdapter`. -* `BehaviorInterceptor`, `Behaviors.monitor`, `Behaviors.withMdc` and @scala[`widen`]@java[`Behaviors.widen`] takes +* Renamed @scala[`widen`]@java[`Behaviors.widen`] to @scala[`transformMessages`]@java[`Behaviors.transformMessages`] +* `BehaviorInterceptor`, `Behaviors.monitor`, `Behaviors.withMdc` and @scala[`transformMessages`]@java[`Behaviors.transformMessages`] takes a @scala[`ClassTag` parameter (probably source compatible)]@java[`interceptMessageClass` parameter]. `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`. diff --git a/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/EventSourcedBehaviorInterceptorSpec.scala b/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/EventSourcedBehaviorInterceptorSpec.scala index e774b64fb5..6a95ab9362 100644 --- a/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/EventSourcedBehaviorInterceptorSpec.scala +++ b/akka-persistence-typed/src/test/scala/akka/persistence/typed/scaladsl/EventSourcedBehaviorInterceptorSpec.scala @@ -83,10 +83,10 @@ class EventSourcedBehaviorInterceptorSpec probe.expectMessage("ABC") } - "be possible to combine with widen" in { + "be possible to combine with transformMessages" in { val probe = createTestProbe[String]() val pid = nextPid() - val ref = spawn(testBehavior(pid, probe.ref).widen[String] { + val ref = spawn(testBehavior(pid, probe.ref).transformMessages[String] { case s => s.toUpperCase() })