how to create multiple chatrooms using websockets in scala?
Asked Answered
F

2

6

I'm trying to learn how to use WebSockets and Akka using the Chat example in the Play for Scala book.

In the book, there is one "ChatRoom" being created, and that's instantiated in the Chat controller with something as simple as this:

val room = Akka.system.actorOf(Props[ChatRoom])

I want to expand this example and have multiple chat rooms available instead of just one. A user can provide a string, which can be a chatroom "name", and that would create a new chatroom. Anyone that tries to join this chatroom would share a broadcast with each other, but not with people in another chatroom. Very similar to IRC.

My questions are the following:

1: How do I create a ChatRoom with a unique name if one does not already exist? 2: How can I check if the existing ChatRoom exists and get a reference to it?

The chatroom name will come via either the URL or a query parameter, that part will be trivial. I'm just not entirely sure how to uniquely identify the Akka ChatRoom and later retrieve that Actor by name.

Fact answered 3/4, 2014 at 10:6 Comment(0)
I
1

You can name actors in Akka, so instead of having:

Akka.system.actorOf(Props[ChatRoom])

You would have:

Akka.system.actorOf(Props[ChatRoom],"room1")

Then, depending on the Akka version you're using, use either Akka.system.actorFor("room1") or Akka.system.actorSelection("room1") to get a reference to the wanted chat room.

Infinitesimal answered 3/4, 2014 at 10:15 Comment(6)
if there are two rooms then how to get room reference.Fact
after naming them differently, you can use the actorFor or actorSelection to get a reference of each room and then send messages to it.Infinitesimal
I am trying this Akka.system.actorOf(Props[ChatRoom],"room1") but when i use Akka.system.actorSelection("room1") the room is not joined by coming member.Fact
actorSelection will just return the ActorRef representing a given room, it is up to you to then send join messages to it.Infinitesimal
val first = Akka.system.actorSelection("room1") (first ? Join(username)).map { case Connected(enumerator) => // Create an Iteratee to consume the feed val iteratee = Iteratee.foreach[JsValue] { event => first ! Talk(username, (event \ "text").as[String]) }.map { _ => first ! Quit(username) } (iteratee,enumerator) } I use this code dut not working.Fact
What is not working? Do you see an error somewhere? Can you edit your question and post the code to the chatroom please?Infinitesimal
C
1

Use the Akka EventBus trait. You can use eventBus and LookupClassification to implement topic based publish-subscribe where the "topic" is the roomID and the subscribers are either the actors for each room or the web-socket actors for each client.

import akka.event.EventBus
import akka.event.LookupClassification

final case class MsgEnvelope(topic: String, payload: Any)

/**
 * Publishes the payload of the MsgEnvelope when the topic of the
 * MsgEnvelope equals the String specified when subscribing.
 */
class LookupBusImpl extends EventBus with LookupClassification {
  type Event = MsgEnvelope
  type Classifier = String
  type Subscriber = ActorRef

  // is used for extracting the classifier from the incoming events  
  override protected def classify(event: Event): Classifier = event.topic

  // will be invoked for each event for all subscribers which registered themselves
  // for the event’s classifier
  override protected def publish(event: Event, subscriber: Subscriber): Unit = {
    subscriber ! event.payload
  }

  // must define a full order over the subscribers, expressed as expected from
  // `java.lang.Comparable.compare`
  override protected def compareSubscribers(a: Subscriber, b: Subscriber): Int =
    a.compareTo(b)

  // determines the initial size of the index data structure
  // used internally (i.e. the expected number of different classifiers)
  override protected def mapSize: Int = 128

}

Then register your actors (in reality you would keep a count of how many users are in each room and subscribe when users enter the room and unsubscribe and kill the actor when no-one is in the room)

val lookupBus = new LookupBusImpl
lookupBus.subscribe(room1Actor, "room1")
lookupBus.subscribe(room2Actor, "room2")

The message will be switched based on the roomID

lookupBus.publish(MsgEnvelope("room1", "hello room1"))
lookupBus.publish(MsgEnvelope("room2", "hello room2"))
lookupBus.publish(MsgEnvelope("room3", "hello dead letter"))
Caboodle answered 25/7, 2015 at 18:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.