From f2adb0a3fd0545caccea102c6ba83ce2dd5efaad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johan=20Andr=C3=A9n?= Date: Mon, 22 May 2017 15:41:19 +0200 Subject: [PATCH] Avoid infinite recursion in AbstractFSM with Java on Scala 2.12 #12887 --- .../java/akka/actor/AbstractFSMActorTest.java | 59 +++++++++++++++++++ .../main/scala/akka/actor/AbstractFSM.scala | 12 ++-- 2 files changed, 65 insertions(+), 6 deletions(-) create mode 100644 akka-actor-tests/src/test/java/akka/actor/AbstractFSMActorTest.java diff --git a/akka-actor-tests/src/test/java/akka/actor/AbstractFSMActorTest.java b/akka-actor-tests/src/test/java/akka/actor/AbstractFSMActorTest.java new file mode 100644 index 0000000000..dc66ececd4 --- /dev/null +++ b/akka-actor-tests/src/test/java/akka/actor/AbstractFSMActorTest.java @@ -0,0 +1,59 @@ +/** + * Copyright (C) 2009-2017 Lightbend Inc. + */ +package akka.actor; + +import akka.testkit.AkkaJUnitActorSystemResource; +import akka.testkit.AkkaSpec; +import akka.testkit.TestProbe; +import akka.testkit.javadsl.TestKit; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; +import org.scalatest.junit.JUnitSuite; + +public class AbstractFSMActorTest extends JUnitSuite { + + public static class MyFSM extends AbstractFSM { + + private final ActorRef probe; + + MyFSM(ActorRef probe) { + this.probe = probe; + onTransition(this::logTransition); + startWith("start", "data"); + when("start", matchEventEquals("next", (newState, data) -> + goTo(newState) + )); + when("next", AbstractFSM.NullFunction()); + initialize(); + } + + private void logTransition(final String s1, final String s2) { + probe.tell(String.format("Transitioning from %1$s to %2$s.", s1, s2), getSelf()); + } + } + + + @ClassRule + public static AkkaJUnitActorSystemResource actorSystemResource = new AkkaJUnitActorSystemResource("AbstractFSMActorTest", + AkkaSpec.testConf()); + + private final ActorSystem system = actorSystemResource.getSystem(); + + @Test + public void canCreateFSM() { + // Coverage for #22887 (failed with Scala 2.12 before fix) + TestProbe probe = new TestProbe(system); + + ActorRef ref = system.actorOf(Props.create(MyFSM.class, probe.ref())); + probe.expectMsg("Transitioning from start to start."); + + ref.tell("next", ActorRef.noSender()); + + probe.expectMsg("Transitioning from start to next."); + } + + +} diff --git a/akka-actor/src/main/scala/akka/actor/AbstractFSM.scala b/akka-actor/src/main/scala/akka/actor/AbstractFSM.scala index 380e300048..9aed845e0e 100644 --- a/akka-actor/src/main/scala/akka/actor/AbstractFSM.scala +++ b/akka-actor/src/main/scala/akka/actor/AbstractFSM.scala @@ -96,7 +96,7 @@ abstract class AbstractFSM[S, D] extends FSM[S, D] { stateName: S, stateTimeout: FiniteDuration, stateFunctionBuilder: FSMStateFunctionBuilder[S, D]): Unit = - when(stateName, stateTimeout)(stateFunctionBuilder.build()) + super.when(stateName, stateTimeout)(stateFunctionBuilder.build()) /** * Set initial state. Call this method from the constructor before the [[#initialize]] method. @@ -119,7 +119,7 @@ abstract class AbstractFSM[S, D] extends FSM[S, D] { * @param timeout state timeout for the initial state, overriding the default timeout for that state */ final def startWith(stateName: S, stateData: D, timeout: FiniteDuration): Unit = - startWith(stateName, stateData, Option(timeout)) + super.startWith(stateName, stateData, Option(timeout)) /** * Add a handler which is called upon each state transition, i.e. not when @@ -129,7 +129,7 @@ abstract class AbstractFSM[S, D] extends FSM[S, D] { * called, not only the first one matching. */ final def onTransition(transitionHandlerBuilder: FSMTransitionHandlerBuilder[S]): Unit = - onTransition(transitionHandlerBuilder.build().asInstanceOf[TransitionHandler]) + super.onTransition(transitionHandlerBuilder.build().asInstanceOf[TransitionHandler]) /** * Add a handler which is called upon each state transition, i.e. not when @@ -139,7 +139,7 @@ abstract class AbstractFSM[S, D] extends FSM[S, D] { * called, not only the first one matching. */ final def onTransition(transitionHandler: UnitApply2[S, S]): Unit = - onTransition(transitionHandler(_: S, _: S)) + super.onTransition(transitionHandler(_: S, _: S)) /** * Set handler which is called upon reception of unhandled messages. Calling @@ -148,14 +148,14 @@ abstract class AbstractFSM[S, D] extends FSM[S, D] { * The current state may be queried using ``stateName``. */ final def whenUnhandled(stateFunctionBuilder: FSMStateFunctionBuilder[S, D]): Unit = - whenUnhandled(stateFunctionBuilder.build()) + super.whenUnhandled(stateFunctionBuilder.build()) /** * Set handler which is called upon termination of this FSM actor. Calling * this method again will overwrite the previous contents. */ final def onTermination(stopBuilder: FSMStopBuilder[S, D]): Unit = - onTermination(stopBuilder.build().asInstanceOf[PartialFunction[StopEvent, Unit]]) + super.onTermination(stopBuilder.build().asInstanceOf[PartialFunction[StopEvent, Unit]]) /** * Create an [[akka.japi.pf.FSMStateFunctionBuilder]] with the first case statement set.