unable to get the value of enumerator in Play 2.5 websocktes - scala

package controllers
import javax.inject._
import play.api._
import play.api.mvc._
import play.api.libs.json._
import play.api.libs.streams._
import akka.stream._
import akka.actor._
import akka.actor.Actor
import akka.actor.ActorSystem
import akka.actor.ActorRef
import akka.actor.Props
import akka.pattern.ask
import akka.util.Timeout
import akka.actor.PoisonPill
import scala.concurrent.duration._
import akka.stream.Materializer
import play.api.cache._
import play.api.libs.iteratee._
import play.api.libs.concurrent.Execution.Implicits.defaultContext
import play.api.libs.concurrent._
import play.api.libs.ws.WSClient
/**
* This controller creates an `Action` to handle HTTP requests to the
* application's home page.
*/
#Singleton
class HomeController #Inject() (cache:CacheApi) (implicit actorSystem:ActorSystem , materializer:Materializer) extends Controller {
def validate(receivedMsg:JsValue,outChannel:Concurrent.Channel[JsValue], privateChannel:Concurrent.Channel[JsValue],outEnumerator:Enumerator[JsValue],privateEnumerator:Enumerator[JsValue]) = {
val user_key = (receivedMsg \ "username").get
val username = user_key.toString().stripSuffix("\"").stripPrefix("\"")
val validate_user :Option[String] = cache.get[String](username)
val valid_result = validate_user.toString()
if(valid_result.equals("None")) {
//cache is not set
// println(valid_result)
//add user
cache.set(username,username)
Ok.withSession(username->username)
//notify all users
val successMsg = Json.parse("""{"username":"Server","message":"A new user has been connected"}""")
outChannel.push(successMsg)
(outEnumerator)
}
else{
//cache is already set
//send error msg to new user
val errorMsg = Json.parse("""{"username":"Server","message":"This username is already taken"}""")
// val (privateEnumerator,privateChannel) = Concurrent.broadcast[JsValue]
privateChannel.push(errorMsg)
(privateEnumerator)
}
}
val (outEnumerator,outChannel) = Concurrent.broadcast[JsValue]; //public stuff
def socket = WebSocket.using[JsValue] {
request => {
val (privateEnumerator,privateChannel) = Concurrent.broadcast[JsValue]
var enumerator = privateEnumerator
var ret = 1;
val inIteratee: Iteratee[JsValue, Unit] = Iteratee.foreach[JsValue](receivedMsg => {
enumerator = validate(receivedMsg,outChannel,privateChannel,outEnumerator,privateEnumerator)
})
(inIteratee, enumerator)
}
}
}
I am new to scala and Play webSockets . I am working in play 2.5.3. Above depending upon the situation , i am trying to get the enumerator of private channel or public channel(i.e. for all connected users). But even if it returns it correctly, i couldn't get it in here (iteratee,enumerator). What am i doing wrong?

Second answer of this post (Broadcasting messages in Play Framework WebSockets) does the same thing.
Create an actor say UserManagerActor who sole purpose is to manage the users and maintain them.
UserManagerActor calls broadcast.
val (enumerator, channel) = Concurrent.broadcast[String].
channel helps in broadcasting the messages to all the users can once using the push method.
Now the actor can manage state of the users in a Map
val users = Map[String, (Enumerator[String],Channel[String])]()
Ensure the actor is killed once all users get disconnected.
Use Iteratee to know is the user is disconnected
Also remove disconnected users to keep the size of the Map manageable.

Related

items fail to be processed in Akka streams app that uses Source.queues and Sink.queues in a flow

I am trying to create an (Akka HTTP) stream procsesing flow using the classes akka.stream.scaladsl.Source and Sink queues.
I am using a queue because I have a processing step in my flow that issues http requests and I want this step to take as many
items off the queue as there are max-open-requests, and stop taking off the queue once max-open-requests are in flight.
The result is that backpressure is applied when my connection pool is overloaded.
Below, I have a very simplified test that reflects the main logic of my app. In the test 'Stress Spec' (below)
I am simulating a number of simultaneous connections via which I will send a 'Source' of 'Requesto' objects
to the getResponses method of the class ServiceImpl.
In the processing step 'pullOffSinkQueue' you will note that I am incrementing a counter to see how many items
I have pulled off the queue.
The test will send Serviceimpl a set of requests whose cardinality is set to equal
streamedRequestsPerConnection * numSimultaneousConnections.
When I send 20 requests my test passes fine. In particular the count of requests pulled off the
Sink.queue will be equal to the number of requests I send out. However, if
I increase the number of requests I send to above 50 or so, I see consistent failures in the test.
I get a message such as the one below
180 was not equal to 200
ScalaTestFailureLocation: com.foo.StressSpec at (StressSpec.scala:116)
Expected :200
Actual :180
<Click to see difference>
This indicates that the number of items pulled off the queue does not equal the number of items put on the queue.
I have a feeling this might be due to the fact that my test is not properly waiting for all items put into the stream
to be processed. If anyone has any suggestions, I'd be all ears ! Code is below.
package com.foo
import java.util.concurrent.atomic.AtomicInteger
import akka.stream.ActorAttributes.supervisionStrategy
import akka.stream.{Attributes, Materializer, QueueOfferResult}
import akka.stream.Supervision.resumingDecider
import akka.stream.scaladsl.{Flow, Keep, Sink, Source}
import scala.concurrent.{ExecutionContext, Future}
import akka.NotUsed
import akka.actor.ActorSystem
import akka.event.{Logging, LoggingAdapter}
import akka.stream.ActorMaterializer
import akka.stream.scaladsl.{Sink, Source}
import org.scalatest.mockito.MockitoSugar
import org.scalatest.{FunSuite, Matchers}
import scala.collection.immutable
import scala.concurrent.duration._
import scala.concurrent.{Await, Future, _}
final case class Responso()
final case class Requesto()
object Handler {
val dbRequestCounter = new AtomicInteger(0)
}
class Handler(implicit ec: ExecutionContext, mat: Materializer) {
import Handler._
private val source =
Source.queue[(Requesto, String)](8, akka.stream.OverflowStrategy.backpressure)
private val sink =
Sink.queue[(Requesto, String)]().withAttributes(Attributes.inputBuffer(8, 8))
private val (sourceQueue, sinkQueue) = source.toMat(sink)(Keep.both).run()
def placeOnSourceQueue(ar: Requesto): Future[QueueOfferResult] = {
sourceQueue.offer((ar, "foo"))
}
def pullOffSinkQueue(qofr: QueueOfferResult): Future[Responso] = {
dbRequestCounter.incrementAndGet()
qofr match {
case QueueOfferResult.Enqueued =>
sinkQueue.pull().flatMap { maybeRequestPair: Option[(Requesto, String)] =>
Future.successful(Responso())
}
case error =>
println("enqueuing error: " + error)
Future.failed(new RuntimeException("enqueuing error: " + error))
}
}
}
class ServiceImpl(readHandler: Handler, writeHandler: Handler)
(implicit log: LoggingAdapter, mat: Materializer) {
private val readAttributeFlow: Flow[Requesto, Responso, NotUsed] = {
Flow[Requesto]
.mapAsyncUnordered(1)(readHandler.placeOnSourceQueue)
.mapAsyncUnordered(1)(readHandler.pullOffSinkQueue)
}
def getResponses(request: Source[Requesto, NotUsed]): Source[Responso, NotUsed] =
request
.via(readAttributeFlow)
.withAttributes(supervisionStrategy(resumingDecider))
}
class StressSpec
extends FunSuite
with MockitoSugar
with Matchers {
val streamedRequestsPerConnection = 10
val numSimultaneousConnections = 20
implicit val actorSystem: ActorSystem = ActorSystem()
implicit val materializer: ActorMaterializer = ActorMaterializer()
implicit val log: LoggingAdapter = Logging(actorSystem.eventStream, "test")
implicit val ec: ExecutionContext = actorSystem.dispatcher
import Handler._
lazy val requestHandler = new Handler()
lazy val svc: ServiceImpl =
new ServiceImpl(requestHandler, requestHandler)
test("can handle lots of simultaneous read requests") {
val totalExpected = streamedRequestsPerConnection * numSimultaneousConnections
def sendRequestAndAwaitResponse(): Unit = {
def getResponses(i: Integer) = {
val requestStream: Source[Requesto, NotUsed] =
Source(1 to streamedRequestsPerConnection)
.map { i =>
Requesto()
}
svc.getResponses(requestStream).runWith(Sink.seq)
}
val responses: immutable.Seq[Future[immutable.Seq[Responso]]] =
(1 to numSimultaneousConnections).map { getResponses(_) }
val flattenedResponses: Future[immutable.Seq[Responso]] =
Future.sequence(responses).map(_.flatten)
Await.ready(flattenedResponses, 1000.seconds).value.get
}
sendRequestAndAwaitResponse()
dbRequestCounter.get shouldBe(totalExpected)
}
}

Getting error while trying to insert data into MongoDB

I am trying to insert data into MongoDB using Play-scala and ReactiveMongo.
Here is my DbimpService.scala:
package services
import models.Post
import reactivemongo.bson.BSONDocument
import reactivemongo.api.MongoDriver
import reactivemongo.api.collections.bson.BSONCollection
import scala.concurrent.ExecutionContext
import javax.inject.Inject
import play.api.libs.json.Json
import reactivemongo.play.json.collection.JSONCollection
import reactivemongo.api.commands.WriteResult
import scala.concurrent.Future
import org.apache.xerces.util.DatatypeMessageFormatter
class Dbimpservice #Inject() (implicit ec:ExecutionContext) extends Dbservice {
def create(p:Post):String={
var status = "Not Saved"
val driver = new MongoDriver
val connection = driver.connection(List("localhost"))
val db = connection("application")
val collection = db[BSONCollection]("post")
val futureList = collection.insert[Post](p)
futureList.onComplete { case sucess => println(sucess) }
return status
}
}
Here is my HomeController.scala:
package controllers
import javax.inject._
import play.api._
import play.api.mvc._
import models._
import scala.util.{ Failure, Success }
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
import reactivemongo.api.{ MongoDriver, MongoConnection }
import reactivemongo.play.json.collection.JSONCollection
import reactivemongo.bson.BSONDocument
import reactivemongo.api.commands.WriteResult
import reactivemongo.api.collections.bson.BSONCollection
import play.api.libs.json.Json
import services.Dbservice
import services.Dbimpservice
import services.Dbservice
import scala.concurrent.ExecutionContext
import scala.concurrent.Await
import scala.concurrent.duration.Duration
/**
* This controller creates an `Action` to handle HTTP requests to the
* application's home page.
*/
#Singleton
class HomeController #Inject() (implicit ec:ExecutionContext,val Dbservice : Dbimpservice)extends Controller {
/**
* Create an Action to render an HTML page with a welcome message.
* The configuration in the `routes` file means that this method
* will be called when the application receives a `GET` request with
* a path of `/`.
*/
def index = Action{
Ok("Hai")
}
def read = Action.async {
val query = BSONDocument()
val driver = new MongoDriver
val connection = driver.connection(List("localhost:27017"))
val db = connection("application")
val collection = db[BSONCollection]("post")
val futureList = collection.find(query).cursor[Post]().collect[List]()
futureList.map { list =>
Ok(list.toString())
}
}
def create = Action(BodyParsers.parse.json) { request =>
val personResult = request.body.validate[Post]
personResult.fold(
errors => {
BadRequest(Json.obj("status " ->"ERROR"))
},
valid = fun
)
}
def fun:Post => Result= { post =>
var ans = Dbservice.create(post)
Ok(ans)
}
}
I am trying to insert the data but not getting inserted and the error which i am getting is
Failure(reactivemongo.core.errors.ConnectionNotInitialized: MongoError['Connection is missing metadata (like protocol version, etc.) The connection pool is probably being initialized.'])
Some one please help me, I even referred the link
http://stackoverflow.com/questions/31456517/embedmongo-with-reactivemongo-process-does-not-exit
but did not get
Guessing that you are using a recent version of ReactiveMongo (0.11.7+), you are using a deprecated DB resolution code (connection(dbName) aka connection.apply(dbName).
See also
You need to use the asynchronous resolution, which benefit from the failover (to handle possible network latency/incident). The following code must so be refactored.
val db = connection("application")
val collection = db[BSONCollection]("post")
val futureList = collection.insert[Post](p)
Using the new DB resolution:
for {
db <- connection.database("application")
collection = db("post")
res <- collection.insert(p)
} yield res

Not able to broadcast the message to all users

package controllers
import javax.inject._
import play.api._
import play.api.mvc._
import play.api.libs.json._
import javax.inject._
import play.api.libs.streams._
import akka.stream._
import akka.actor._
import akka.actor.Actor
import akka.actor.ActorSystem
import akka.actor.ActorRef
import akka.actor.Props
import akka.pattern.ask
import akka.util.Timeout
import akka.actor.PoisonPill
import scala.concurrent.duration._
import akka.stream.Materializer
import play.api.cache._
import play.api.libs.iteratee._
import play.api.libs.concurrent.Execution.Implicits.defaultContext
import play.api.libs.concurrent._
import play.api.libs.ws.WSClient
//import play.api.cache.
/**
* This controller creates an `Action` to handle HTTP requests to the
* application's home page.
*/
#Singleton
class HomeController #Inject() (cache:CacheApi) (implicit actorSystem:ActorSystem,materializer:Materializer) extends Controller {
/**
* Create an Action to render an HTML page with a welcome message.
* The configuration in the `routes` file means that this method
* will be called when the application receives a `GET` request with
* a path of `/`.
*/
class MyWebSocketActor(out:ActorRef) extends Actor {
def receive = {
case msg:JsValue => {
val (returnMsg,returnCode) = validate(msg)
if(returnCode==404) {
out ! returnMsg
out !PoisonPill
} else if (returnCode==200) {
//send to all
val (enumerator,outChannel) = Concurrent.broadcast[JsValue]
val iteratee:Iteratee[JsValue,Unit] = Iteratee.foreach[JsValue]{returnMsg =>
outChannel.push(returnMsg)
}
println(returnMsg)
}
}
}
}
def validate(msg:JsValue) = {
val user_key = (msg \ "username").get
val username = user_key.toString().stripSuffix("\"").stripPrefix("\"")
val validate_user: Option[String] = cache.get[String](username)
val valid_result = validate_user.toString()
if (valid_result.equals("None")) {
//cache is not set
// println(valid_result)
//add user
cache.set(username,username)
Ok.withSession(username->username)
//notify all users
val successMsg = Json.parse("""{"username":"Server","message":"A new user has been connected"}""")
(successMsg, 200)
}
else {
//cache is already set
//send error msg to new user
val errorMsg = Json.parse("""{"username":"Server","message":"This username is already taken"}""")
(errorMsg, 404)
}
}
object MyWebSocketActor {
def props(out: ActorRef) = Props(new MyWebSocketActor(out))
}
def socket = WebSocket.accept[JsValue, JsValue] { request =>
ActorFlow.actorRef(out => MyWebSocketActor.props(out))
}
}
I am new to Akka and play websocket. I am working in Play 2.5.3. In the above code in receive function i am trying to get all the channels connected by val (enumerator,outChannel) = Concurrent.broadcast[JsValue] so that I can broadcast a message to all connected users. But no message is passed to any user but the println(returnMsg) is executed. What am I doing wrong?

Spray cache for get service?

i am using cache headers like no-cache and no-store, i don´t know to do application level caching (maybe i could need some documentation here)
i print in console the data result when i call the method in mongodb, but it only works once after i run my app(that ocur in get service), the second time my app doesn´t print nothing, that is, it doesn't call the method.... that ocur when i try get a list of users the second time... for example, when i post something like insert new a user. i need to see the changes in the db in the frontend, my app seems get data from cache and it does´nt call the method to get the users again
the code I use in my spray scala service is
`package api
import spray.routing.Directives
import akka.actor.ActorRef
import spray.http.MediaTypes._
import core.UserRegister
import core.User
import scala.concurrent.ExecutionContext
import core.{User, LoginActor}
import akka.util.Timeout
import LoginActor._
import spray.http._
import scala.Some
import spray.json.JsonFormat
import spray.json.RootJsonFormat
import spray.json.JsArray
import spray.json.CollectionFormats
import spray.json._
import DefaultJsonProtocol._
import scala.util.parsing.json.JSONArray
import scala.util.parsing.json._
import data.UserDao
import core.UserListActor
import spray.routing.Route
import core.CoreActors
import spray.routing.HttpService
import core.Core
import akka.actor.{Props, ActorRefFactory, ActorSystem}
import akka.actor.ActorContext
import spray.routing.HttpServiceActor
import spray.http.HttpHeaders.RawHeader
import scala.concurrent.duration.Duration
import spray.routing.authentication.BasicAuth
import spray.routing.directives.CachingDirectives._
import spray.httpx.encoding._
import spray.caching._
import spray.caching.{LruCache, Cache}
import spray.caching.Cache
import web.StaticResources
import scala.concurrent.Future
class ListarUsuarioService(listaUsuario: ActorRef)(implicit executionContext: ExecutionContext)
extends Directives with DefaultJsonFormats with SprayCORSsupport with CORSSupport{
import akka.pattern.ask
import scala.concurrent.duration._
implicit val userFormat = jsonFormat2(User)
implicit val registerFormat = jsonFormat1(Register)
implicit val userRegisterFormat = jsonFormat5(UserRegister)
implicit val registeredFormat = jsonObjectFormat[Registered.type]
implicit val notRegisteredFormat = jsonObjectFormat[NotRegistered.type]
implicit val system = ActorSystem()
import system.dispatcher
lazy val simpleRouteCache = routeCache()
lazy val simpleCache = routeCache(maxCapacity = 5000, timeToIdle = 0.001 hour)
//lazy val cache = LruCache()
def routeCache(maxCapacity: Int = 2000, initialCapacity: Int = 100, timeToLive: Duration = 5 seconds,
timeToIdle: Duration = Duration.Inf): Cache[RouteResponse] =
LruCache(maxCapacity, initialCapacity, timeToLive, timeToIdle)
// and a Cache for its result type
val cache2: Cache[Double] = LruCache()
val listaUsuariosroute:Route =
cache(routeCache()){
cors{ addCORSDefaultSupport(){
path("usuario") {
get {
respondWithMediaType(`application/json`) {
_.complete {
//Elemento de la lista
//listaUsuarios(1)
UserListActor.listaUsuarios.toJson.convertTo[JsArray].prettyPrint }
}
}
}
}
}
}//cors
}
`
I am using Cors and cache headers like no-store, public and no-cache, but it doesn´t works, even i clear my cache browser but it neither works

Akka Actors - Creating Pool of Actors

I created the following Akka Actor code in Scala. Code works fine when a single workerActor is created. But code silently fails when I try to create a pool of worker actors using round robin logic. Any idea how to fix this? How do I get more debug info to be printed?
import scala.collection.immutable.Map
import scala.collection.mutable.ArrayBuffer
import akka.actor.actorRef2Scala
import akka.actor.ActorSystem
import akka.actor.Props
import scala.concurrent.Await
import scala.concurrent.duration._
import akka.pattern.ask
import akka.util.Timeout
import akka.actor._
import org.junit._
import org.junit.Assert._
import messaging.actors._
import akka.routing.RoundRobinRouter
import akka.routing._
class MainEngineActorTest {
#Test
def testMainActor () = {
val _system = ActorSystem("MainEngineActor")
val master = _system.actorOf(Props[MainEngineActor], name = "EngineActor")
println ("Created Main Engine Actor")
implicit val timeout = Timeout(5 seconds)
val userID = new UserID ("test1")
println ("Sending messages")
for (i <- ( 1 to 10)) {
master ! "Hello"
master ! "World"
}
}
}
class MainEngineActor extends Actor with ActorLogging{
// works if we create only a single workerActor
//val workerActors = context.actorOf(Props[WorkerActor], name = "WorkerActors")
// Doesn't work when we create a pool of worker actors - how do we fix this?
// why doesn't this work and why aren't any error messages printed?
val workerActors = context.actorOf(RoundRobinPool(5).props(Props[WorkerActor]), name = "WorkerActors")
def receive: Receive = {
case request => {
workerActors forward request
}
}
}
class WorkerActor extends Actor {
def receive: Receive = {
case request => {
println ("RequestReceived =" + request)
}
}
}
Try creating your pool like this instead:
val workerActors = context.actorOf(Props[WorkerActor].withRouter(RoundRobinPool(5)), name = "WorkerActors")
In addition, when running this as a Junit test, the program is terminating before the child actors have a chance to receive the message. I verified this by adding a Thread.sleep(5000) after the loop that is sending the Hello and World messages to master. I then tweaked your code a little bit to use Akka's TestActorRef from akka-testkit which will force everything to use the CallingThreadDispatcher to get synchronous execution throughout the test and everything works as expected. The two lines I changed are:
implicit val _system = ActorSystem("MainEngineActor")
val master = TestActorRef(new MainEngineActor())