I have an issue that I don't have any log output in my program. Please, help me solve the problem and tell me where I did mistake. The app is meant to be a Chat app. It consists UserActor, Main, RoomActor.
Main:
package Chat
import akka.actor.typed.{ActorRef, ActorSystem, Behavior}
import akka.actor.typed.scaladsl.Behaviors
object Main extends App {
sealed trait MainActor
def apply(): Behavior[MainActor] = Behaviors.setup { context =>
val system: ActorSystem[Nothing] = ActorSystem(Behaviors.empty, "ChatSystem")
val userActor = system.systemActorOf(UserActor(), "userActor")
val roomActor = system.systemActorOf(RoomActor(), "roomActor")
userActor ! UserActor.CreateUser("John", "Doe", 25, system.deadLetters)
val roomName = "General"
roomActor ! RoomActor.CreateRoom(userActor, roomName)
userActor ! UserActor.SendMessageToRoom(roomActor, "Hello, everyone!", "John")
Thread.sleep(5000)
system.terminate()
Behaviors.receiveMessage {
case _ =>
context.log.info("Unknown message for MainActor")
Behaviors.same
}
}
}
UserActor:
package Chat
import Chat.RoomActor.RoomCommand
import akka.actor.typed.{ActorRef, Behavior}
import akka.actor.typed.scaladsl.Behaviors
import java.util.UUID
object UserActor {
sealed trait UserCommand
case class CreateUser(name: String, surname: String, age: Int, userActor: ActorRef[UserCommand]) extends UserCommand
case class User(name: String, surname: String, age: Int, userId: String, userActor: ActorRef[UserCommand])
case class SendMessageToRoom(room: ActorRef[RoomCommand], content: String, userName: String) extends UserCommand
case class LeftRoomNotification(user: ActorRef[UserCommand], roomName: String) extends UserCommand
case class JoinRoomNotification(user: ActorRef[UserCommand], roomName: String) extends UserCommand
case class ReceiveMessage(sender: ActorRef[UserCommand], content: String) extends UserCommand
def apply(): Behavior[UserCommand] = userBehavior(Map.empty)
private def userBehavior(users: Map[String, User]): Behavior[UserCommand] =
Behaviors.receive{(context, message) =>
message match {
case ReceiveMessage(sender, content) =>
context.log.info(s"User ${context.self} received message from $sender: $content")
Behaviors.same
case SendMessageToRoom(room, content, userName) =>
users.get(userName) match {
case Some(user) =>
room ! RoomActor.BroadcastMessage(user.userActor, content)
case None =>
throw new Exception(s"Unknown User $userName!")
}
Behaviors.same
case CreateUser(name, surname, age, userActor) =>
val userId = UUID.randomUUID().toString
val newUser = User(name, surname, age, userId, userActor)
context.log.info(s"User $newUser added in user list")
context.log.info(s"Created user: $userId")
users + (userId -> newUser)
Behaviors.same
case LeftRoomNotification(user, roomName) =>
context.log.info(s"User $user received LeftRoomNotification for room: $roomName")
Behaviors.same
case JoinRoomNotification(user, roomName) =>
context.log.info(s"User $user received JoinRoomNotification for room: $roomName")
Behaviors.same
case _ =>
context.log.warn("Unknown message for UserActor")
Behaviors.same
}
}
}
RoomActor:
package Chat
import Chat.UserActor.{JoinRoomNotification, UserCommand}
import akka.actor.typed.scaladsl.Behaviors
import akka.actor.typed.{ActorRef, Behavior}
import java.time.LocalDateTime
import scala.collection.mutable
object RoomActor {
sealed trait RoomCommand
case class CreateRoom(user: ActorRef[UserCommand], roomName: String) extends RoomCommand
case class LeaveRoom(user: ActorRef[UserCommand], roomName: String) extends RoomCommand
case class JoinRoom(user: ActorRef[UserCommand], roomName: String) extends RoomCommand
case class BroadcastMessage(user: ActorRef[UserCommand], content: String) extends RoomCommand
case class Room(roomName: String, owner: ActorRef[UserCommand], creationDate: LocalDateTime, users: mutable.Set[ActorRef[UserCommand]])
def apply(): Behavior[RoomCommand] = roomBehavior(Map.empty)
private def roomBehavior(rooms: Map[String, Room]): Behavior[RoomCommand] =
Behaviors.receive { (context, message) =>
message match {
case CreateRoom(user, roomName) =>
val room = Room(roomName, user, LocalDateTime.now(), mutable.Set(user))
context.log.info(s"$user created room: $roomName")
roomBehavior(rooms + (roomName -> room))
case LeaveRoom(user, roomName) =>
rooms.get(roomName).foreach { room =>
room.users -= user
if(user == room.owner) {
if(room.users.isEmpty) {
context.log.info(s"$user was the owner and the last user in room: $roomName. Removing the room.")
roomBehavior(rooms - roomName)
} else {
val newOwner = room.users.head
context.log.info(s"$user was the owner. Assigning the new owner: $newOwner")
val updatedRoom = room.copy(owner = newOwner)
roomBehavior(rooms + (roomName -> updatedRoom))
}
} else {
context.log.info(s"$user left the room: $roomName")
for (elem <- room.users) {
elem.tell(UserActor.LeftRoomNotification(user, roomName))
}
Behaviors.same
}
}
context.log.info(s"$user left room: $roomName")
Behaviors.same
case JoinRoom(user, roomName) =>
rooms.get(roomName).foreach {room =>
room.users += user
user ! JoinRoomNotification(user, roomName)
}
Behaviors.same
case BroadcastMessage(sender, content) =>
rooms.values.foreach{room =>
room.users.foreach{user =>
if(user != sender) {
user ! UserActor.ReceiveMessage(sender, content)
}
}
}
context.log.info(s"Broadcast message from $sender: $content")
Behaviors.same
case _ =>
throw new IllegalArgumentException("Unknown message for RoomActor")
Behaviors.same
}
}
}
The output is:
15:24:35: Executing ':Main.main()'...
> Task :compileJava NO-SOURCE
> Task :compileScala
[Warn] : -target is deprecated: Use -release instead to compile against the correct platform API.
one warning found
> Task :processResources NO-SOURCE
> Task :classes
> Task :Main.main()
Deprecated Gradle features were used in this build, making it incompatible with Gradle 9.0.
You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins.
For more on this, please refer to https://docs.gradle.org/8.5/userguide/command_line_interface.html#sec:command_line_warnings in the Gradle documentation.
BUILD SUCCESSFUL in 13s
2 actionable tasks: 2 executed
15:24:49: Execution finished ':Main.main()'.
I've added Thread.sleep(5000), but the program still doesn't have any log output. I tried to add logback.xml with root level = "DEBUG" and "info". Also, I tried replace Thread.sleep by Await.result(system.whenTerminated, Duration.Inf)
Because you aren't running anything.
Behaviors.xx
only creates a factory of actors, you have to explicitly let ActorSystem
Spawn it. This is an example:
object Main extends App {
sealed trait MainActor
// setup your guardian actor here
val guardianActor: Behavior[Any] = Behaviors.receive { (context, message) =>
// like java constructor here
val userActor = context.spawn(UserActor(), "userActor")
// other...
// react to message
message match {
case _ =>
context.log.info("Unknown message for MainActor")
Behaviors.same
}
}
// start ActorSystem here...
val system: ActorSystem[Nothing] = ActorSystem(guardianActor, "ChatSystem")
}
One more thing, system.systemActorOf
isn't recommended for the user, you need to create from guardian actor, which means the root (user) actor of ActorSystem.
If you want to create actor from anywhere, use the SpawnProtocol
Actually, the actor system itself is a root actor of JVM.