3

After receiving a Future[httpResponse] I'm trying to send the message to the sender but I'm loosing the reference of the sender.

Here is the code of my receive method:

    def receive = {
        case Seq(method: HttpMethod, endpoint: String, payload: String) ⇒ {
          // I have the correct sender reference
          implicit val materializer: ActorMaterializer = ActorMaterializer(ActorMaterializerSettings(context.system)) // needed by singleRequest method below
          // I have the correct sender reference

          val response: Future[HttpResponse] = Http(context.system).singleRequest(HttpRequest(method = method, uri = endpoint, entity = payload))
          println("http request sent")
          // I have the correct sender reference
          response onSuccess {
            case HttpResponse(statusCode, _, entity, _) ⇒ {
              entity.dataBytes.runFold(ByteString.empty)(_ ++ _).foreach { body ⇒ 
                // NO Reference to sender
                sender ! HttpConsumerResponse(statusCode = statusCode, contentType = entity.contentType, body = body.utf8String)
              }
            }
            case _ => println("http request success 2")
          }

          response onFailure {
            case exception: Throwable ⇒ {
              println("http request failure")
              throw exception
            } // Adopting let-it-crash fashion by re-throwning the exception
          }
        }
        case _ => println("I am httpConsumerActor and I don't know")
      }

If I change the code like this:

def receive = {
        case Seq(method: HttpMethod, endpoint: String, payload: String) ⇒ {
          // I have the correct sender reference
          implicit val materializer: ActorMaterializer = ActorMaterializer(ActorMaterializerSettings(context.system)) // needed by singleRequest method below
          // I have the correct sender reference

          val response: Future[HttpResponse] = Http(context.system).singleRequest(HttpRequest(method = method, uri = endpoint, entity = payload))
          println("http request sent")
          // I have the correct sender reference
          val mySender = sender
          response onSuccess {
            case HttpResponse(statusCode, _, entity, _) ⇒ {
              entity.dataBytes.runFold(ByteString.empty)(_ ++ _).foreach { body ⇒ 
                // NO Reference to sender
                mySender ! HttpConsumerResponse(statusCode = statusCode, contentType = entity.contentType, body = body.utf8String)
              }
            }
            case _ => println("http request success 2")
          }

          response onFailure {
            case exception: Throwable ⇒ {
              println("http request failure")
              throw exception
            } // Adopting let-it-crash fashion by re-throwning the exception
          }
        }
        case _ => println("I am httpConsumerActor and I don't know")
      }

everything works, but I have to send the reference of the actor like with this line and I know this is not the best way to do this:

val mySender = sender
Ramesh Maharjan
  • 41,071
  • 6
  • 69
  • 97
Jean
  • 5,201
  • 11
  • 51
  • 87

1 Answers1

8

The reason your fist approach does not work is that you are "closing over mutable state", i.e. the sender() method is executed when the onComplete is executed, and does not contain the reference anymore. This is a fairly common mistake in Akka, we have all been there ! :)

The right solution, as you already found out yourself is to prestore the reference. There are other options such as "becoming" something else, but for your use I'd say that the pre-store is the right approach in terms of tradeoff between "nice" and "simple".

For reference see these resources: SO question, Blog post

Diego Martinoia
  • 4,592
  • 1
  • 17
  • 36
  • 1
    Additional reference, the Akka docs: http://doc.akka.io/docs/akka/current/scala/additional/faq.html#sender-getsender-disappears-when-i-use-future-in-my-actor-why- – johanandren Jun 13 '17 at 14:39