简体   繁体   中英

scala.MatchError on actor

During the test, I've got the following error message:

[ERROR] [07/13/2019 14:25:39.659] [DetectorSystem3-akka.io.pinned-dispatcher-2] [akka://DetectorSystem3/user/DetectorSupervisor] SapActiveConfirmed (of class com.sweetsoft.detector.ServerMonitoring$SapActiveConfirmed$)
scala.MatchError: SapActiveConfirmed (of class com.sweetsoft.detector.ServerMonitoring$SapActiveConfirmed$)
    at com.sweetsoft.detector.DetectorStateMachine$.$anonfun$create$2(DetectorStateMachine.scala:20)
    at akka.actor.typed.internal.BehaviorImpl$ReceiveMessageBehavior.receive(BehaviorImpl.scala:53)
    at akka.actor.typed.Behavior$.interpret(Behavior.scala:437)
    at akka.actor.typed.Behavior$.interpretMessage(Behavior.scala:393)
    at akka.actor.typed.internal.adapter.ActorAdapter.handleMessage(ActorAdapter.scala:121)
    at akka.actor.typed.internal.adapter.ActorAdapter.aroundReceive(ActorAdapter.scala:102)
    at akka.actor.ActorCell.receiveMessage(ActorCell.scala:612)
    at akka.actor.ActorCell.invoke(ActorCell.scala:581)
    at akka.testkit.CallingThreadDispatcher.process$1(CallingThreadDispatcher.scala:280)
    at akka.testkit.CallingThreadDispatcher.runQueue(CallingThreadDispatcher.scala:313)
    at akka.testkit.CallingThreadDispatcher.dispatch(CallingThreadDispatcher.scala:234)
    at akka.actor.dungeon.Dispatch.sendMessage(Dispatch.scala:158)
    at akka.actor.dungeon.Dispatch.sendMessage$(Dispatch.scala:152)
    at akka.actor.ActorCell.sendMessage(ActorCell.scala:447)

I am using akka typed and in my opinion it should not happen like this.

The actor is implemented as follows:

object DetectorStateMachine {

  case class State(kafka: ServerHealthEvent, sap: ServerHealthEvent)

  def create(informant: ActorRef[InformantEvent]): Behavior[ServerHealthEvent] =
    Behaviors.setup { context =>

      context.log.info(s"=============> Start DetectorStateMachine <=============")

      def loop(state: State): Behavior[ServerHealthEvent] = {

        Behaviors.receiveMessage {
          case KafkaActiveConfirmed
            if state.kafka == KafkaActiveConfirmed || state.sap == SapActiveConfirmed =>
            informant ! ServerOnlineConfirmed
            loop(state)
          case k@KafkaActiveConfirmed if state.kafka == KafkaInactiveConfirmed =>
            loop(State(k, state.sap))
          case k@KafkaInactiveConfirmed =>
            informant ! ServerOfflineConfirmed
            loop(State(k, state.sap))
          case SapActiveConfirmed
            if state.sap == SapActiveConfirmed && state.kafka == KafkaActiveConfirmed =>
            informant ! ServerOnlineConfirmed
            loop(state)
          case s@SapActiveConfirmed if state.sap == SapInactiveConfirmed =>
            loop(State(state.kafka, s))
          case s@SapInactiveConfirmed =>
            informant ! ServerOfflineConfirmed
            loop(State(state.kafka, s))
        }

      }

      loop(State(KafkaInactiveConfirmed, SapInactiveConfirmed))


    }

}

and the test is implemented as:

final class DetectorSpec extends BddSpec {

  private val sap = new SapMock()
    .withExposedPorts(8080)
    .waitingFor(Wait.forHttp("/"))

  private val kafka = new KafkaContainer("5.2.1")

  sap.start()
  kafka.start()

  override def afterAll(): Unit = {
    sap.stop()
    kafka.stop()
  }

  private def withKafkaOfflineSapOnline(testCode: TestProbe[ServerEvent] => Unit)
  : Unit = {

    val config = ConfigFactory.parseString(
      s"""akka.actor.default-dispatcher = {
            type = akka.testkit.CallingThreadDispatcherConfigurator
          }
          akka.actor.testkit.typed.single-expect-default = 0s
          kafka {
            servers = "PLAINTEXT://localhost:9092"
          }
          sap {
            server = "ws://${sap.getContainerIpAddress}:${sap.getMappedPort(8080)}"
          }""")

    val testKit = ActorTestKit("DetectorSystem3", config)
    val inbox = testKit.createTestProbe[ServerEvent]("Receiver")
    testKit.spawn(DetectorSupervisor.create(), "DetectorSupervisor")
    testKit.system.receptionist ! Receptionist.Register(ServerStateKey, inbox.ref)

    testCode(inbox)
    testKit.shutdownTestKit()
  }

    scenario("SAP is online and Kafka is offline") {
      withKafkaOfflineSapOnline { inbox =>
        Given("I am waiting for the current state message")
        When("I am receive the state message")
        val msg = inbox.receiveMessage(60.second)
        Then("it should contain `Kafka is offline`")
        msg should be(ServerOfflineApproved)
      }
    }

  }
} 

What am I doing wrong?

Well, you have two branches with SapActiveConfirmed :

case SapActiveConfirmed
  if state.sap == SapActiveConfirmed && state.kafka == KafkaActiveConfirmed =>
  ...
case s@SapActiveConfirmed if state.sap == SapInactiveConfirmed =>
  ...

So if neither condition holds (eg state is State(KafkaInactiveConfirmed, SapActiveConfirmed) ) then you'll get a MatchError . And withKafkaOfflineSapOnline certainly sounds like it can produce this state...

It might help if you structure the match differently:

Behaviors.receiveMessage {
  case KafkaActiveConfirmed =>
    ...
  case KafkaInactiveConfirmed =>
    ...
  ...
}

where the conditions go inside the branches.

如果您想避免匹配错误-最好添加带有错误打印输出的后备空白案例

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM