Could not find implicit value for ActorSystem - scala

I'm just starting out with Akka and Scala and I'm trying to connect to a WebSocket using Akka Streams. I've created my SocketActor below and I try to instantiate from the main method.
Here is my SocketActor:
package com.lightbend.akka.sample
import akka.actor.{Actor, Props}
import akka.Done
import akka.http.scaladsl.Http
import akka.stream.scaladsl._
import akka.http.scaladsl.model.ws._
import scala.concurrent.Future
object SocketActor {
def props(coinApiIdentifier: String): Props = Props(new SocketActor(coinApiIdentifier))
case object Start
case object Stop
}
class SocketActor(val ticker: String) extends Actor {
import SocketActor._
// Future[Done] is the materialized value of Sink.foreach,
// emitted when the stream completes
private val incoming: Sink[Message, Future[Done]] =
Sink.foreach[Message] {
case message: TextMessage.Strict =>
println(message.text)
}
// send this as a message over the WebSocket
private val outgoing = Source.single(TextMessage("hello world!"))
// flow to use (note: not re-usable!)
private val webSocketFlow = Http().webSocketClientFlow(WebSocketRequest("wss://api.com/v1/"))
// the materialized value is a tuple with
// upgradeResponse is a Future[WebSocketUpgradeResponse] that
// completes or fails when the connection succeeds or fails
// and closed is a Future[Done] with the stream completion from the incoming sink
private val graph =
outgoing
.viaMat(webSocketFlow)(Keep.right) // keep the materialized Future[WebSocketUpgradeResponse]
.toMat(incoming)(Keep.both) // also keep the Future[Done]
override def receive: PartialFunction[Any, Unit] = {
case Start =>
println("Start message received.")
graph.run()
}
}
And my main method:
object AkkaQuickstart extends App {
// Create the 'helloAkka' actor system
val system: ActorSystem = ActorSystem("test")
val materializer: ActorMaterializer = ActorMaterializer()
val socketActor: ActorRef =
system.actorOf(SocketActor.props("hello"), "socket-actor")
socketActor ! Start
}
Unfortunately, I get the error:
Error:(38, 35) could not find implicit value for parameter system:
akka.actor.ActorSystem private val webSocketFlow =
Http().webSocketClientFlow(WebSocketRequest("wss://api.com/v1/"))
I've tried passing some implicit parameters to the constructor of the SocketActor but that didn't work too well. It seems like the ActorSystem is not in scope for some reason. How can I get my system in scope for the Http() function in SocketActor?

Define an implicit val:
class SocketActor(val ticker: String) extends Actor {
implicit val sys = context.system
// ...
}
This will provide the implicit ActorSystem that the Http object expects.
There is another issue with your code: the stream in your actor will not run because there is no materializer in scope. One way to address this is to create the materializer inside the actor:
class SocketActor(val ticker: String) extends Actor {
implicit val sys = context.system
implicit val mat = ActorMaterializer()(context)
// ...
}
Note that if the materializer were defined as implicit val mat = ActorMaterializer(), it would implicitly use context.system because of the implicit val sys = context.system. Instead, the materializer is created with the actor's context explicitly. This is done because of the warning in the documentation:
Do not create new actor materializers inside actors by passing the context.system to it. This will cause a new ActorMaterializer to be created and potentially leaked (unless you shut it down explicitly) for each such actor. It is instead recommended to either pass-in the Materializer or create one using the actor’s context.
The recommended approach, which allows the creator of the actor to reuse a materializer, is to pass the materializer to the actor as an implicit parameter:
class SocketActor(val ticker: String)(implicit val mat: ActorMaterializer) extends Actor {
implicit val sys = context.system
// ...
}
Then you could pass the materializer in the main program to this actor:
object AkkaQuickstart extends App {
implicit val system: ActorSystem = ActorSystem("test")
implicit val materializer: ActorMaterializer = ActorMaterializer()
val socketActor: ActorRef =
system.actorOf(Props(classOf[SocketActor], "hello", materializer), "socket-actor")
socketActor ! Start
}

Related

How to instantiate a materializer for AkkaStreams when I have a reference to the typed actor system?

The code below does not compile, it says that ActorMaterializer is missing an implicit ActorRefFactory. How should I provide one?
val guardian: Behavior[Done] = Behaviors.setup(_ => {
Behaviors.receiveMessage{
case Done => Behaviors.stopped
}
})
implicit val sys = ActorSystem(guardian, "sys")
implicit val materializer: Materializer = ActorMaterializer()
Previous answers are halfways to the indended new API.
The typed ActorSystem can be implicitly transformed to provide the system materialiser as well, so just having it available as an implicit should be enough.
For example:
Behaviors.setup { ctx =>
implicit val system = ctx.system
Source(1 to 10).runForeach(println)
...
}
The inner working of this is an implicit conversion available from the Materializer companion object that extracts the system materializer of a ClassicActorSystemProvider into a Materializer. Both the typed and the classic ActorSystem implements ClassicActorSystemProvider.
Akka Streams at this point requires a "classic" (untyped) ActorSystem which can be implicitly converted into a materializer.
So if materializing a stream inside an Akka Typed Behavior, one would
implicit val materializer = context.classicActorContext.system
And if materializing a stream outside of an actor but where you have a typed ActorSystem:
implicit val materializer = typedActorSystem.classicSystem
As mentioned by #johanandren, one can also put the Typed ActorSystem in the implicit scope, which will allow the implicit conversion to Materializer to take effect.
implicit val system = context.system
The Akka documentation for 2.6.x indicates that ActorMaterializer is deprectated: "Use the system wide materializer or Materializer.apply(actorContext) with stream attributes or configuration settings to change defaults."
Use this instead:
import akka.Done
import akka.actor.ActorSystem
import akka.actor.typed.Behavior
import akka.actor.typed.scaladsl.Behaviors
import akka.stream.Materializer
object Main {
def main(Args: Array[String]) : Unit = {
val guardian: Behavior[Done] = Behaviors.setup(_ => {
Behaviors.receiveMessage{
case Done => Behaviors.stopped
}
})
val as = ActorSystem()
val materializer = Materializer(as)
}
}

Send request at regular interval

Hello I am trying to learn akka-http and make a simple client for a rest api. I would like to know how to send a request to a rest server at regular interval.
I have my simple client:
object RestClientApp extends App {
// set up ActorSystem and other dependencies here
//#main-class
//#server-bootstrapping
implicit val system: ActorSystem = ActorSystem()
implicit val materializer: ActorMaterializer = ActorMaterializer()
//#server-bootstrapping
// needed for the future flatMap/onComplete in the end
implicit val executionContext = system.dispatcher
val responseFuture: Future[HttpResponse] = Http().singleRequest(HttpRequest(uri = "http://akka.io"))
responseFuture
.onComplete {
case Success(res) => println(res)
case Failure(_) => sys.error("something wrong")
}
}
How can I send a request and process the response every x time unit ?
A natural fit would be Akka Streams, on which Akka HTTP is built. The example below uses a repeating Source to make an HTTP request to a given URI every five seconds. The materialized stream prints the responses.
import akka.actor.ActorSystem
import akka.http.scaladsl.Http
import akka.http.scaladsl.model._
import akka.stream.ActorMaterializer
import akka.stream.scaladsl.Source
import scala.concurrent.duration._
object RestClient {
def main(args: Array[String]): Unit = {
implicit val materializer = ActorMaterializer()
Source
.repeat(HttpRequest(uri = "http://akka.io"))
.throttle(1, 5.seconds)
.mapAsync(1)(Http().singleRequest(_))
.runForeach(println)
}
}
you can define RestClient as an actor and use actorSystem.schedule to schedule the actor.
final def schedule(
initialDelay: FiniteDuration,
interval: FiniteDuration,
receiver: ActorRef,
message: Any)(implicit
executor: ExecutionContext,
sender: ActorRef = Actor.noSender): Cancellable =
Example,
import akka.actor.{Actor, ActorSystem, Props}
class RestClientActor extends Actor {
import RestClientActor._
import scala.util.Success
import scala.util.Failure
import akka.http.scaladsl.Http
import akka.http.scaladsl.model.HttpRequest
implicit val actorSystem: ActorSystem = context.system
import context.dispatcher
override def receive: Receive = {
case InitiateRequest =>
Http().singleRequest(HttpRequest(uri = "http://jsonplaceholder.typicode.com/posts")).onComplete {
case Success(s) => println(s._3)
case Failure(f) => println(f.getMessage)
}
}
}
object RestClientActor {
case object InitiateRequest
}
object RestClientApp {
def main(args: Array[String]): Unit = {
import akka.actor.ActorSystem
import scala.concurrent.duration._
import RestClientActor._
import scala.concurrent.ExecutionContextExecutor
implicit val system: ActorSystem = ActorSystem()
implicit val executionContext: ExecutionContextExecutor = system.dispatcher
val actor = system.actorOf(Props[RestClientActor], "RestClientActor")
system.scheduler.schedule(1 seconds, 3 seconds, actor, InitiateRequest)
}
}

How to use http client in an Akka actor that has a service?

I have an akka actor, and I would like to use a simple service inside that actor. That service should use the client side api's singleRequest method to fetch something from the local network.
My Actor:
package actor
import actor.WorkerActor._
import akka.actor.Actor
import service.HealthCheckService
import scala.concurrent.ExecutionContext
object WorkerActor {
case object HealthCheck
}
class WorkerActor extends Actor {
implicit val system = context.system
implicit val ec: ExecutionContext = context.system.dispatcher
val healthCheckService = new HealthCheckService()
override def receive: Receive = {
case HealthCheck => sender ! healthCheckService.execute()
}
}
Here I created an ActorSystem and an ExecutionContext as well, so that my service can use it:
package service
import akka.actor.ActorSystem
import akka.http.scaladsl.Http
import akka.http.scaladsl.model.{HttpRequest, HttpResponse}
import scala.concurrent.{ExecutionContext, Future}
import scala.util.{Failure, Success}
class HealthCheckService(implicit ec: ExecutionContext, implicit val system: ActorSystem) {
def execute() = {
val responseFuture: Future[HttpResponse] = Http().singleRequest(HttpRequest(uri = "http://someRandom.url"))
and do something with the response....
}
}
If I don't pass an executionContext into the service I get the error:
[error] Cannot find an implicit ExecutionContext. You might pass
[error] an (implicit ec: ExecutionContext) parameter to your method
[error] or import scala.concurrent.ExecutionContext.Implicits.global.
And if I don't pass an actorsystem into the service I get the error:
[error] could not find implicit value for parameter system: akka.actor.ActorSystem
[error] val responseFuture: Future[HttpResponse] = Http().singleRequest(HttpRequest(uri = "http://someRandom.url"))
Questions:
How should services be correctly used from an Actor?
Is it correct to pass around the ActorSystem and ExecutionContext, why doesn't it happen
under the hood?
Http.apply signature is
def apply()(implicit system: ActorSystem): HttpExt
so you need to pass ActorSystem to it implicitly or explicitly.
I do not see anything that requires ExecutionContext inside service, so suppose you do something with Future you get from singleRequest call. Any method on Future will require execution context.
So code is correct, but could be simplified a little:
class WorkerActor extends Actor {
// you do not need to declare implicit val if you just need to pass it once
val healthCheckService = new HealthCheckService(context.system)
override def receive: Receive = {
case HealthCheck => sender ! healthCheckService.execute()
}
}
class HealthCheckService(implicit val system: ActorSystem) {
// dispatcher is already implicit so you need just import it, not declare another val
import system.dispatcher
def execute() = {
val responseFuture: Future[HttpResponse] = Http().singleRequest(HttpRequest(uri = "http://someRandom.url"))
and do something with the response....
}
}
Also, from your code for service I have an impression that you manipulate somehow with future, probably adding await (as soon as your service per your question does not really need ActorSystem), instead using akka and piping messages to actors from Future. Probably (as answer on you first question), you need to check later example of using singleRequest with actors from link to akka-http you added in question .

Is it possible to make an Akka HTTP core client request inside an Actor?

Below is a simple actor which is required to make a HTTP call to receive data from an API. According to the Akka HTTP Core Request-Level Client-Side API only the ActorSystem and ActorMaterializer are needed implicitly.
class MyActor extends Actor {
import context.system
implicit val materializer = ActorMaterializer()
override def receive: Receive = {
case _ => {
val responseFuture: Future[HttpResponse] = Http().singleRequest(HttpRequest(uri = "http://akka.io"))
responseFuture onComplete {
case Success(res) => println(res)
case Failure(t) => println("An error has occured: " + t.getMessage)
}
}
}
}
However, when attempting to compile the application I receive the following error messages:
Error:(18, 48) ambiguous implicit values: both value context in trait Actor of type => akka.actor.ActorContext and method system in trait ActorContext of type => akka.actor.ActorSystem match expected type akka.actor.ActorRefFactory
implicit val materializer = ActorMaterializer()
Error:(18, 48) implicit ActorRefFactory required: if outside of an Actor you need an implicit ActorSystem, inside of an actor this should be the implicit ActorContext
implicit val materializer = ActorMaterializer()
Error:(18, 48) not enough arguments for method apply: (implicit context: akka.actor.ActorRefFactory)akka.stream.ActorMaterializer in object ActorMaterializer. Unspecified value parameter context.
implicit val materializer = ActorMaterializer()
Error:(22, 70) could not find implicit value for parameter fm: akka.stream.Materializer
val responseFuture: Future[HttpResponse] = Http().singleRequest(HttpRequest(uri = "http://akka.io"))
Error:(22, 70) not enough arguments for method singleRequest: (implicit fm: akka.stream.Materializer)scala.concurrent.Future[akka.http.scaladsl.model.HttpResponse]. Unspecified value parameter fm.
val responseFuture: Future[HttpResponse] = Http().singleRequest(HttpRequest(uri = "http://akka.io"))
Error:(23, 22) Cannot find an implicit ExecutionContext. You might pass an (implicit ec: ExecutionContext) parameter to your method or import scala.concurrent.ExecutionContext.Implicits.global.
responseFuture onComplete {
Error:(23, 22) not enough arguments for method onComplete: (implicit executor: scala.concurrent.ExecutionContext)Unit. Unspecified value parameter executor.
responseFuture onComplete {
Is this the correct way to make a HTTP call inside an Akka Actor?
Edit
Included import ExecutionContext.Implicits.global to fix the last two ExecutionContext errors.
An implicit ActorRefFactory is required to create the ActorMaterializer. The context property defined in the Actor trait extends ActorRefFactory, and it's implicit. The system property on context, which you imported explicitly, is another implicit candidate for ActorRefFactory, because ActorSystem extends ActorRefFactory.
My suggestion is removing the import and passing it explicitly where it is needed.
class MyActor extends Actor {
// Do not import context.system
// import context.system
implicit val materializer = ActorMaterializer()
override def receive: Receive = {
case _ => {
// use context.system explicitly
val responseFuture: Future[HttpResponse] = Http(context.system)
.singleRequest(HttpRequest(uri = "http://akka.io"))
responseFuture onComplete {
case Success(res) => println(res)
case Failure(t) => println("An error has occured: " + t.getMessage)
}
}
}
}

Scala spray-client define implicits for AKKA ActorRefFactory

I'm trying to write a simple HTTP client using Scala and spray-client. I'm basing my client on the examples given on Spray docs.
My issue is that the example is creating a new implicit ActorSystem i.e.
implicit val system = ActorSystem()
but I want my client to be reusable and not create a new ActorSystem.
Here's the gist of my code.
trait WebClient {
def get(url: String)(implicit system: ActorSystem): Future[String]
}
object SprayWebClient extends WebClient {
val pipeline: HttpRequest => Future[HttpResponse] = sendReceive
def get(url: String): Future[String] = {
val r = pipeline (Get("http://some.url/"))
r.map(_.entity.asString)
}
}
But I am getting two compiler errors regarding implicits
implicit ActorRefFactory required: if outside of an Actor you need an implicit ActorSystem, inside of an actor this should be the implicit ActorContext WebClient.scala ...
and
not enough arguments for method sendReceive: (implicit refFactory: akka.actor.ActorRefFactory, implicit executionContext: scala.concurrent.ExecutionContext, implicit futureTimeout: akka.util.Timeout)spray.http.HttpRequest => scala.concurrent.Future[spray.http.HttpResponse]. Unspecified value parameters refFactory, executionContext. WebClient.scala...
How should I change the API definitions?
Here's one solution:
import akka.actor.ActorSystem
import spray.http.{HttpRequest, HttpResponse}
import scala.concurrent.Future
import spray.client.pipelining._
trait WebClient {
def get(url: String): Future[String]
}
class SprayWebClient(implicit system: ActorSystem) extends WebClient {
import system.dispatcher
val pipeline: HttpRequest => Future[HttpResponse] = sendReceive
def get(url: String): Future[String] = {
val r = pipeline (Get("http://some.url/"))
r.map(_.entity.asString)
}
}
and here's another that keeps the original WebClient.get signature:
import akka.actor.ActorSystem
import spray.http.{HttpRequest, HttpResponse}
import scala.concurrent.Future
import spray.client.pipelining._
trait WebClient {
def get(url: String)(implicit system: ActorSystem): Future[String]
}
object SprayWebClient extends WebClient {
def get(url: String)(implicit system: ActorSystem): Future[String] = {
import system.dispatcher
val pipeline: HttpRequest => Future[HttpResponse] = sendReceive
val r = pipeline (Get("http://some.url/"))
r.map(_.entity.asString)
}
}
The second one is a bit more expensive because the pipeline is created anew every time even if it is theoretically static per ActorSystem. I would prefer the first solution and try to find a way to propagate the WebClient through your application (by using the cake pattern, by passing it around explicitly, or by using other dependency injection techniques).