Akka. Is there a way to share code between two receives? - scala

Let's say I have several receives and use become() unbecome() to switch between them. And all receives have some common message handling. Like this:
class Test extends Actor {
override def receive: Receive = first
def first: Receive = {
case "common" => // how to share?
println("common stuff")
case "second" =>
println("move to second")
context.become(second)
}
def second: Receive = {
case "common" => // how to share?
println("common stuff")
case "first" =>
println("move to first")
context.become(first)
}
}
Right now I see two ways:
Duplicate pattern match on "common" and
handle it in some function like handleCommon()
Merge receives like
this, but it doesn't seem right to me.
Here is an example of the second one:
class Test extends Actor {
override def receive: Receive = merge(common, first)
def common: Receive = {
case "common" =>
println("common stuff")
}
def first: Receive = {
case "second" =>
println("move to second")
context.become(merge(common, second))
}
def second: Receive = {
case "first" =>
println("move to first")
context.become(merge(common, first))
}
private def merge(functions: Receive*): Receive = {
case any =>
val fOpt = functions find (_.isDefinedAt(any))
if (fOpt.isDefined) {
fOpt foreach (f => f(any))
} else {
unhandled(any)
}
}
}
I'm sure there have to be a better way to do this. Is there?

You can use orElse:
def common: Receive = {
case "common" =>
println("common stuff")
}
def first: Receive = ({
case "second" =>
println("move to second")
context.become(second)
}: Receive) orElse common
def second: Receive = ({
case "first" =>
println("move to first")
context.become(first)
}: Receive) orElse common
Note that you have to explicitly annotate the type at the end of the expression because otherwise scala will infer Function1 and not Receive.

Related

Why are Futures in scala realised using a collection in the companion object?

I'm not sure whether I chose the right title for my question..
I'm interested as to why the collection in the companion object is defined. Am I mistaken that this collection will have only one f in it? What I am seeing is a collection with exactly one element.
Here's the Future I'm dealing with:
trait Future[+T] { self =>
def onComplete(callback: Try[T] => Unit): Unit
def map[U](f: T => U) = new Future[U] {
def onComplete(callback: Try[U] => Unit) =
self onComplete (t => callback(t.map(f)))
}
def flatMap[U](f: T => Future[U]) = new Future[U] {
def onComplete(callback: Try[U] => Unit) =
self onComplete { _.map(f) match {
case Success(fu) => fu.onComplete(callback)
case Failure(e) => callback(Failure(e))
} }
}
def filter(p: T => Boolean) =
map { t => if (!p(t)) throw new NoSuchElementException; t }
}
Its companion object:
object Future {
def apply[T](f: => T) = {
val handlers = collection.mutable.Buffer.empty[Try[T] => Unit]
var result: Option[Try[T]] = None
val runnable = new Runnable {
def run = {
val r = Try(f)
handlers.synchronized {
result = Some(r)
handlers.foreach(_(r))
}
}
}
(new Thread(runnable)).start()
new Future[T] {
def onComplete(f: Try[T] => Unit) = handlers.synchronized {
result match {
case None => handlers += f
case Some(r) => f(r)
}
}
}
}
}
In my head I was imagining something like the following instead of the above companion object (notice how I replaced the above val handlers .. with var handler ..):
object Future {
def apply[T](f: => T) = {
var handler: Option[Try[T] => Unit] = None
var result: Option[Try[T]] = None
val runnable = new Runnable {
val execute_when_ready: Try[T] => Unit = r => handler match {
case None => execute_when_ready(r)
case Some(f) => f(r)
}
def run = {
val r = Try(f)
handler.synchronized {
result = Some(r)
execute_when_ready(r)
}
}
}
(new Thread(runnable)).start()
new Future[T] {
def onComplete(f: Try[T] => Unit) = handler.synchronized {
result match {
case None => handler = Some(f)
case Some(r) => f(r)
}
}
}
}
}
So why does the function execute_when_ready leads to stackoverflow, but that's not the case with handlers.foreach? what is the collection is offering me which I can't do without it? And is it possible to replace the collection with something else in the companion object?
The collection is not in the companion object, it is in the apply method, so there is a new instance for each Future. It is there because there can be multiple pending onComplete handlers on the same Future.
Your implementation only allows a single handler and silently removes any existing handler in onComplete which is a bad idea because the caller has no idea if a previous function has added an onComplete handler or not.
As noted in the comments, the stack overflow is because execute_when_ready calls itself if handler is None with no mechanism to stop the recursion.

Handling a case of when my future returns a None in my play controller action method,

I want to refactor by update action below to look a little more readable and also handle the failure case better
The userService has the following functions:
class UserService {
def getUserByUsername: Future[Option[Int]] // which is the UserId
def getUserById: Future[User]
}
My action looks like:
def update(userId: Int) = Action.async { implicit request =>
request.body.validate[User] match {
case JsSuccess(user, _) => {
userService.getUserByUsername(user.username).map { userId =>
userService.getUserById(userId.get).map { existingUser =>
userService.update(user.username)
Ok
}
}
}
case JsError(err) => Future.sucessful(BadRequest(err))
}
}
How do I handle the situation where getUserByUsername returns a None?
Would this look cleaner if it was in a for comprehension, is it better style?
You have some missing data in your questions such as case classes for the User model, userService class.
also better to attach the original function.
Anyways, I will do something as follows:
def update(userId: Int) = Action { implicit request =>
request.body.validate[User] match {
case JsSuccess(user: User, _) => {
val userId = getUserByUsername(user.username)
userId match {
case Some(userId) => {
for {
_ <- userService.getUserById(userId)
_ <- userService.update(user.username)
} yield Ok
}.recover {
case t: Throwable =>
Metrics.errOnUpdate.increment() // Some metric to monitor
logger.error(s"update userId: $userId failed with ex: ${t.getMessage}") // log the error
InternalServerError(Json.toJson(Json.obj("error" -> "Failure occured on update"))) // return custom made exception to the client
}
case None => Future.successful(NotFound(s"No such user with ${user.username}"))
}
}
case JsError(err) => Future.sucessful(BadRequest(err))
}
}
Note: If .update returns Future, you actually not waiting to update before returning Ok to the user, thus, if its fails, its still returns Ok.
To fix that, use flatMap and then map the value of update response.
You can also separate the recovering for the getUserById and update if you prefer.
Edit:
def update(userId: Int) = Action { implicit request =>
request.body.validate[User] match {
case JsSuccess(user: User, _) => {
getUserByUsername(user.username).flatMap {
case Some(userId) => for {
_ <- userService.getUserById(userId)
_ <- userService.update(user.username)
} yield Ok
case None => Future.successful(NotFound(s"No such user with ${user.username}"))
}
}.recover {
case t: Throwable =>
Metrics.errOnUpdate.increment() // Some metric to monitor
logger.error(s"update userId: $userId failed with ex: ${t.getMessage}") // log the error
InternalServerError(Json.toJson(Json.obj("error" -> "Failure occured on update"))) // return custom made exception to the client
}
}
case JsError(err) => Future.sucessful(BadRequest(err))
}
}
First, you probably need to use Option.fold:
#inline final def fold[B](ifEmpty: => B)(f: A => B)
Then you can do something like this:
def update(userId: Int) = Action.async { implicit request =>
def handleJsonErrors(errors: Seq[(JsPath, collection.Seq[JsonValidationError])]): Future[Result] = ???
def updateUser(userWithoutId: User): Future[Result] = {
for {
userId <- userService.getUserByUsername(userWithoutId.username)
_ <- userService.getUserById(userId.get)
_ <- userService.update(userWithoutId.username)
} yield {
Ok
}
}
request.body.asJson.fold {
Future.successful(BadRequest("Bad json"))
} {
_.validate[User].fold(handleJsonErrors, updateUser).recover {
case NonFatal(ex) =>
InternalServerError
}
}
}

How to get message from method

I want to rewrite console IO application (sum counting) to messenger-bot. The StdIn.readLine() let me get the next input number in recursion.
object HelloCountBot extends TelegramBot with Polling with Commands {
def go(number: Long, chatId: Long): IO[Long] =
for {
input <- ??? /*here I need get the next number*/
acc <- input.toLong match {
case 0L => sendMessageMethodIO(chatId, "The sum is:") *> IO(0L)
case _ => go(number + 1, chatId)
}
acc <- IO(acc + input.toLong)
} yield acc
/*input point for every new message*/
override def onMessage(message: Message) = message.text match {
case Some(text) if text == "start" => go(1, message.chat.id)
.unsafeRunSync
}
def main(args: Array[String]): Unit = HelloCountBot.run()
}
How to organize code to get the next message inside recursion from method onMessage with Unit return.
I didn't find in https://github.com/bot4s/telegram any methods for receiving messages in the way that you want. So I think the best option is to create stateful bot as shown in this example: https://github.com/bot4s/telegram/blob/master/examples/src/StatefulBot.scala.
So if I understand your code correctly, it can be reorganized in the following way (trait PerChatState is taken from the link above):
object HelloCountBot
extends TelegramBot
with Polling
with Commands
with PerChatState[Long] {
override def onMessage(message: Message): Future[Unit] = {
implicit val msg = message
message.text match {
case Some(text) if text == "start" =>
Future {
setChatState(0L)
}
case Some(value) if value == "0" =>
withChatState {
sum =>
reply(s"The sum is ${sum.getOrElse(0L)}")
.map(_ => clearChatState)
}
case Some(value) =>
withChatState {
mayBeSum =>
Future {
mayBeSum.foreach(
sum => setChatState(sum + value.toLong)
)
}
}
}
}
def main(args: Array[String]): Unit = HelloCountBot.run()
}
It uses Futures, but you can rewrite it to IO, if you prefer.

Scala Play WebSocket - One Actor, multiple message types

What I want to do is basically 1:1 this: Scala Play Websocket - use one out actor to send both: Array[Byte] and String messages
Sadly the API has changed a lot since 2.4 (I am on 2.6.0-M4 right now).
What I tried (does not compile for obvious reasons):
WebSocket.accept[WSMessage, WSMessage]
{
request =>
ActorFlow.actorRef
{
out => WebSocketActor.props(out)
}
}
sealed trait WSMessage
case class StringMessage(s: String) extends WSMessage
case class BinaryMessage(a: Array[Byte]) extends WSMessage
case class JsonMessage(js: JsValue) extends WSMessage
object MyMessageFlowTransformer
{
implicit val WSMessageFlowTransformer: MessageFlowTransformer[WSMessage, WSMessage] =
{
new MessageFlowTransformer[WSMessage, WSMessage]
{
def transform(flow: Flow[WSMessage, WSMessage, _]) =
{
AkkaStreams.bypassWith[Message, WSMessage, Message](Flow[Message] collect
{
case StringMessage(s) => Left(s)
case BinaryMessage(b) => Left(b)
case JsonMessage(j) => Left(j)
case _ => Right(CloseMessage(Some(CloseCodes.Unacceptable)))
})(flow map WSMessage.apply)
}
}
}
}
I am somewhat lost. play.api.http.websocket.Message is a sealed trait, and probably for a good reason...
Define a MessageFlowTransformer[Either[String, Array[Byte]], Either[String, Array[Byte]]]:
type WSMessage = Either[String, Array[Byte]]
implicit val mixedMessageFlowTransformer: MessageFlowTransformer[WSMessage, WSMessage] = {
new MessageFlowTransformer[WSMessage, WSMessage] {
def transform(flow: Flow[WSMessage, WSMessage, _]) = {
AkkaStreams.bypassWith[Message, WSMessage, Message](Flow[Message].collect {
case BinaryMessage(data) => Left(Right(data.toArray))
case TextMessage(text) => Left(Left(text))
})(flow map {
case Right(data) => BinaryMessage.apply(ByteString.apply(data))
case Left(text) => TextMessage.apply(text)
})
}
}
}

Scala Play Websocket - use one out actor to send both: Array[Byte] and String messages

I have Play websockets action:
def socket = WebSocket.acceptWithActor[String, Array[Byte]] { request => out =>
Props(new WebSocketInActor(out))
}
Generally I need to send to browser large raw arrays of data. But sometimes I need to send some small string data. In browser I can detect is data in text format or raw ArrayBuffer.
If I create actor that sends String, I can send string messages, If I create actor that sends with Array[Byte], I can send raw arrays. Both situations I don't need to change client code. So, how can I force Play to use both sending methods with one out actor?
Ah, those answers that comes just after you post question on SO. Looking through reference and sourcecode, I found that there is mixedFrame FrameFromatter: https://github.com/playframework/playframework/blob/2.4.x/framework/src/play/src/main/scala/play/api/mvc/WebSocket.scala#L75
So you just need to say that you will respond with Either[String, Array[Byte]] and if you want to send string use Left(somestring) or else use Right[somearray].
class WebSocketInActor(out: ActorRef) extends Actor {
override def preStart() = {
println("User connected")
val s = "Hello"
out ! Left(s)
out ! Right(s.getBytes("utf8"))
}
override def postStop() = {
println("User discconnected")
}
def receive = {
case msg: String => {
}
case _ =>
}
}
def socket = WebSocket.acceptWithActor[String, Either[String, Array[Byte]]] { request => out =>
Props(new WebSocketInActor(out))
}
UPDATE:
Or you can go one step further and create your own frame formatter
sealed trait WSMessage
case class StringMessage(s: String) extends WSMessage
case class BinaryMessage(a: Array[Byte]) extends WSMessage
case class JsonMessage(js: JsValue) extends WSMessage
implicit object myFrameFormatter extends BasicFrameFormatter[WSMessage] {
private val textFrameClass = classOf[TextFrame]
private val binaryFrameClass = classOf[BinaryFrame]
def toFrame(message: WSMessage): BasicFrame = message match {
case StringMessage(s) => TextFrame(s)
case BinaryMessage(a) => BinaryFrame(a)
case JsonMessage(js) => TextFrame(Json.stringify(js))
}
def fromFrame(frame: BasicFrame): WSMessage = frame match {
case TextFrame(s) => StringMessage(s)
case BinaryFrame(a) => BinaryMessage(a)
}
def fromFrameDefined(clazz: Class[_]): Boolean = clazz match {
case `textFrameClass` => true
case `binaryFrameClass` => true
case _ => false // shouldn't be reachable
}
}
class WebSocketInActor(out: ActorRef) extends Actor {
override def preStart() = {
println("User connected")
val s = "Hello"
val a:Array[Byte] = Array(100, 50, 30).map(_.toByte)
out ! StringMessage(s)
out ! JsonMessage(Json.obj("txt" -> s, "array" -> a))
out ! BinaryMessage(a)
}
override def postStop() = {
println("User discconnected")
}
def receive = {
case msg: String => {
}
case _ =>
}
}
def socket = WebSocket.acceptWithActor[String, WSMessage] { request => out =>
Props(new WebSocketInActor(out))
}