Search code examples
scalaakkafsm

Why is data always empty in this akka FSM actor?


Here is the code:

package com.packt.akka

import akka.actor.{ActorSystem, FSM, Props, Stash}

object UserStorageFSM {

  // FSM State
  sealed trait State

  case object Connected extends State

  case object Disconnected extends State

  // FSM Data
  sealed trait Data {
    def data: List[User]
  }

  case object EmptyData extends Data {
    val data = List.empty
  }


  trait DBOperation

  object DBOperation {

    case object Create extends DBOperation

    case object Update extends DBOperation

    case object Read extends DBOperation

    case object Delete extends DBOperation

  }

  case object Connect

  case object Disconnect

  case class Operation(op: DBOperation, user: User)

  case class User(username: String, email: String)

}


class UserStorageFSM extends FSM[UserStorageFSM.State, UserStorageFSM.Data] with Stash {

  import UserStorageFSM._

  // 1. define start with
  startWith(Disconnected, EmptyData)

  // 2. define states
  when(Disconnected) {
    case Event(Connect, _) =>
      println("UserStorage Connected to DB")
      unstashAll()
      goto(Connected) using (EmptyData)
    case Event(_, _) =>
      stash()
      stay using (EmptyData)
  }

  when(Connected) {
    case Event(Disconnect, _) =>
      println("UserStorage disconnected from DB")
      goto(Disconnected) using EmptyData

    case Event(Operation(op, user), oldData) =>
      op match {
        case DBOperation.Create =>
          stay using new Data {
            override def data = user :: oldData.data
          }
        case DBOperation.Delete => stay using new Data {
          override def data = oldData.data.filter(_ != user)
        }
        case DBOperation.Read => {
          println(oldData.data)
          stay using oldData
        }
        case DBOperation.Update => {
          stay using new Data {
            override def data: List[User] = user :: oldData.data.filter(_.username != user.username)
          }
        }
      }
      stay using EmptyData
  }

  // 3. initialize
  initialize()

}


object FiniteStateMachine extends App {

  import UserStorageFSM._

  val system = ActorSystem("Hotswap-FSM")

  val userStorage = system.actorOf(Props[UserStorageFSM], "userStorage-fsm")


  userStorage ! Connect
  userStorage ! Operation(DBOperation.Create, User("Admin", "admin@packt.com"))
  userStorage ! Operation(DBOperation.Create, User("Admin1", "admin@packt.com"))
  userStorage ! Operation(DBOperation.Read, User("Admin", "admin@packt.com"))
  userStorage ! Disconnect

  Thread.sleep(1000)

  system.terminate()

}

It simulates a storage system that allows CRUD operations. The problem here is that the system seems to always contain empty data. What went wrong here?


Solution

  • In your handler for Operation you call stay in the pattern match on op, but then at the bottom you call stay using EmptyData and that is the one that gets used. Remove stay using EmptyData from the bottom of case Event(Operation(op, user), oldData) => and you should start seeing the updated Data.