Scala, Akka: pattern matching for object in trait issue

喜夏-厌秋 提交于 2020-01-07 05:42:31

问题


Good day. I'm making a simple program which check's some server state and faced the issue with pattern matching. Here is the code: Entry point:

object Run extends App with StateActor.Api{
  private implicit val system = ActorSystem()
  implicit val blockingDispatcher: MessageDispatcher = system.dispatchers.lookup("blocking-dispatcher")
  protected val log: LoggingAdapter = Logging(system, getClass)
  protected implicit val materializer: ActorMaterializer = ActorMaterializer()

  import scala.concurrent.duration._
  implicit val timeout = Timeout(17 seconds)

  val listener = system.actorOf(StateActor.props)
  system.scheduler.schedule(
    0 milliseconds,
    5 minutes,
    listener,
    Ping
  )
}

Actor:

class StateActor(implicit val blockingDispatcher: MessageDispatcher) extends Actor with StateActor.Api with ActorLogging {

  import akka.pattern.pipe
  private val formatter = JSONFormat.defaultFormatter
  private val mHookUrl = ...

  var mState: State = UNDEFINED
  override def receive: Receive = {
    case Ping =>
      log.debug("Ping")
      Future(Http("http://...").timeout(15000, 15000).asString)
        .map {
          case HttpResponse(_, 200, _) => UpResponse
          case HttpResponse(body, code, _) => DownResponse(s"Code: $code, body:\n $body")
          case rest => DownResponse(s"Undefined object: ${rest.toString}")
        } recover { case e => DownResponse(e.getMessage) } pipeTo self

    case UpResponse =>
      if (mState == DOWN || mState == UNDEFINED) {
        mState == UP
        reportToSlack("Client Up")
      }

    case DownResponse(reason) =>
      if (mState == UP || mState == UNDEFINED) {
        mState == DOWN
        reportToSlack(s"Client DOWN!\n Reason: $reason")
      }
    case other =>
      println(other)
      println(other.getClass)
  }

  def reportToSlack(message: String): Unit = {
    ...
  }
}

object StateActor {
  trait Api {
    case object Ping

    sealed trait State
    case object UP extends State
    case object DOWN extends State
    case object UNDEFINED extends State

    sealed trait StateMessage
    case object UpResponse extends StateMessage
    case class DownResponse(reason: String) extends StateMessage
  }

  def props(implicit blockingDispatcher: MessageDispatcher) = Props(new StateActor())
}

As you can see, I put all messages and other stuff intoto trait "API" inside "StateActor" companion object. But when scheduler sends "Ping" to actor, it matches 'case other', not 'case Ping'. Problem can be solved just by moving 'case object Ping' out from trait and companion object and making it 'stand alone' object. Like this:

case object Ping
object StateActor {
      trait Api {
        ...
    }
    ...
}

But why it doesn't work when it's inside trait? All other case classes and objects in trait pattern match just fine.


回答1:


Run and StateActor both extend the trait separately, so each has its own Ping object and they shouldn't match. The only reason other messages match is because the StateActor is sending them to itself! It wouldn't even work with two different StateActor instances.

Instead of

moving 'case object Ping' out from trait and companion object

you should make Api an object and make the messages accessible by importing them: import StateActor.Api._ instead of extends StateActor.Api (or put them directly into object StateActor).



来源:https://stackoverflow.com/questions/44578165/scala-akka-pattern-matching-for-object-in-trait-issue

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!