Error while calling an API using akka in scala - scala

TO start with I am very new to Scala and also I don't have any Java experience also. I am trying to call an API using simple Scala code and running into the errors. Code looks like:
import akka.actor.ActorSystem
import akka.http.scaladsl.Http
import akka.http.scaladsl.model._
import akka.stream.ActorMaterializer
import scala.concurrent.Future
import scala.util.{ Failure, Success }
object Client {
def main(args: Array[String]): Unit = {
implicit val system = ActorSystem()
implicit val materializer = ActorMaterializer()
// needed for the future flatMap/onComplete in the end
implicit val executionContext = system.dispatcher
val responseFuture: Future[HttpResponse] = Http().singleRequest(HttpRequest(uri = "https://akka.io"))
responseFuture
.onComplete {
case Success(res) => println(res)
case Failure(_) => sys.error("something wrong")
}
}
}
Basically, I have just copied the code from Akka documentation and trying to run it.
I get the following errors:
not found: value ActorSystem
implicit val system = ActorSystem()
not found: value ActorMaterializer
implicit val materializer = ActorMaterializer()
Cannot find an implicit ExecutionContext. You might pass
an (implicit ec: ExecutionContext) parameter to your method.
Also 'ActorMaterializer()' now seems to be deprecated. Is this the reason for errors?
Thanks in advance :)

You may need to add akka-streams as a dependency in your build tool.
ActorMaterializer.apply should just be a warning, unrelated to your other error. Might be worth opening an issue on github asking for updated docs for this snippet though.

This should get you going.
In your build.sbt
scalaVersion := "2.13.8"
val akkaVersion = "2.6.19"
val akkaHTTPVersion = "10.2.9"
libraryDependencies ++= Seq(
"com.typesafe.akka" %% "akka-stream" % akkaVersion,
"com.typesafe.akka" %% "akka-actor" % akkaVersion,
"com.typesafe.akka" %% "akka-http" % akkaHTTPVersion
)
The code below is from the latest Doc, with the added line to consume the response entity as described in the Doc.
object HttpClientSingleRequest extends App {
implicit val system: ActorSystem = ActorSystem()
import system.dispatcher
val responseFuture: Future[HttpResponse] = Http().singleRequest(HttpRequest(uri = "https://akka.io"))
responseFuture
.onComplete {
case Success(res) =>
// Even if we don’t care about the response entity, we must consume it
res.entity.discardBytes()
println(s"Success: ${res.status}")
case Failure(ex) => sys.error(s"Something wrong: ${ex.getMessage}")
}
}

Related

Akkatype with classic actors giving me an error: Unsupported access to ActorContext from outside of Actor

I am getting the following runtime error in my akka application (using both akka typed and classic)
java.lang.UnsupportedOperationException: Unsupported access to
ActorContext from the outside of
Actor[akka://my-classic-actor-system/user/ChatServer#1583147696]. No
message is currently processed by the actor, but ActorContext was
called from
Thread[my-classic-actor-system-akka.actor.default-dispatcher-5,5,run-main-group-0].
def main(args: Array[String]): Unit = {
val logger = LoggerFactory.getLogger(getClass)
logger.debug("Server starting yo...")
implicit val system = akka.actor.ActorSystem("my-classic-actor-system")
implicit val typedGuardian: ActorRef[Any] =
system.spawn(HttpWebsocketServer(), "ChatServer")
val client = system.actorOf(Client.props(remote), "clientActor")
//..
}
The source of the error is inside of HttpWebsocketServer which is taken from (
https://github.com/johanandren/chat-with-akka-http-websockets/blob/akka-2.6/src/main/scala/chat/Server.scala#L101):
object HttpWebsocketServer {
def apply(): Behavior[Any] = {
Behaviors.setup[Any] { context =>
implicit val ec: ExecutionContext = context.executionContext
val system = context.system
val chatRoom = context.spawn(ChatRoom(), "chat")
val userParent = context.spawn(SpawnProtocol(), "users")
val maker =
context.spawn(websock.Maker(), "mmaker")
val route =
path("chat") {
get {
handleWebSocketMessages(
newUserFlow(system, chatRoom, userParent)
)
}
}
// needed until Akka HTTP has a 2.6 only release
implicit val materializer: Materializer =
SystemMaterializer(context.system).materializer
implicit val classicSystem: akka.actor.ActorSystem =
context.system.toClassic
Http()
.bindAndHandle(route, "0.0.0.0", 9000)
// future callback, be careful not to touch actor state from in here
.onComplete {
case Success(binding) =>
context.log.debug(
s"Started server at ${binding.localAddress.getHostString}:${binding.localAddress.getPort}"
)
case Failure(ex) =>
ex.printStackTrace()
context.log.error("Server failed to start, terminating")
context.system.terminate()
}
Behaviors.empty
}
}
}
build.sbt
val AkkaVersion = "2.6.18"
val AkkaHttpVersion = "10.2.7"
libraryDependencies ++= Seq(
"io.netty" % "netty-all" % "4.1.68.Final",
"com.typesafe.akka" %% "akka-actor" % AkkaVersion,
"com.typesafe.akka" %% "akka-actor-typed" % AkkaVersion,
"com.typesafe.akka" %% "akka-stream" % AkkaVersion,
"com.typesafe.akka" %% "akka-stream-typed" % AkkaVersion,
"com.typesafe.akka" %% "akka-http" % AkkaHttpVersion,
"com.typesafe.akka" %% "akka-http-spray-json" % AkkaHttpVersion,
"ch.qos.logback" % "logback-classic" % "1.2.10",
scalaTest % Test
)
If I comment out below in my code I don't get the runtime error, so it looks like that is the source of the issue:
implicit val materializer: Materializer =
SystemMaterializer(context.system).materializer
implicit val classicSystem: akka.actor.ActorSystem =
context.system.toClassic
Http()
.bindAndHandle(route, "0.0.0.0", 9000)
// future callback, be careful not to touch actor state from in here
.onComplete {
case Success(binding) =>
context.log.debug(
s"Started server at ${binding.localAddress.getHostString}:${binding.localAddress.getPort}"
)
case Failure(ex) =>
ex.printStackTrace()
context.log.error("Server failed to start, terminating")
context.system.terminate()
}
I'm not sure what I can do to solve this issue, I am creating actors from either the system or child actors.
You are not allowed to use context outside of an actor. And you do it in callback of your future.
case Success(binding) =>
context.log.debug(
s"Started server at ${binding.localAddress.getHostString}:${binding.localAddress.getPort}"
)
case Failure(ex) =>
ex.printStackTrace()
context.log.error("Server failed to start, terminating")
context.system.terminate()
The problem is that actor context is treated as internal state of an actor and must not be accessed outside of the scope. Even your comment says that
// future callback, be careful not to touch actor state from in here
The solution is to pre-reference your log and system (you have this already) in a val outside of the callback. The callback can look like
case Success(binding) =>
log.debug(
s"Started server at ${binding.localAddress.getHostString}:${binding.localAddress.getPort}"
)
case Failure(ex) =>
ex.printStackTrace()
log.error("Server failed to start, terminating")
system.terminate()

Exception while using Play WS to access a REST WebService

I want to use the Play WS Standalone (outside a Play Project) to call a simple Hello World RESTfull Web Service.
I'm Using an SBT Poject in Intelij IDEA whith this configuration :
name := "ScalaSbtProject"
version := "1.0"
scalaVersion := "2.11.7"
libraryDependencies += "com.typesafe.play" % "play-ws_2.11" % "2.5.10"
Here's my code :
import akka.actor.ActorSystem
import akka.stream.ActorMaterializer
import play.api.libs.ws.ahc.AhcWSClient
import scala.util.{Failure, Success}
object Test {
def main(args: Array[String]): Unit = {
import scala.concurrent.ExecutionContext.Implicits._
implicit val actor = ActorSystem()
implicit val materializer = ActorMaterializer()
val wsClient = AhcWSClient()
val response = wsClient
.url("http://localhost:8080/WebService_war_exploded/service/test")
.get()
response.onComplete {
case Success(re) => { println(re.body)}
case Failure(e) => { e.getStackTrace.foreach(println)}
}
wsClient.close()
}
}
which throws this Exception :
Response Is Failed :
http://localhost:8080
org.asynchttpclient.netty.channel.NettyConnectListener.onFailure(NettyConnectListener.java:160)
org.asynchttpclient.netty.request.NettyChannelConnector$1.onFailure(NettyChannelConnector.java:103)
org.asynchttpclient.netty.SimpleChannelFutureListener.operationComplete(SimpleChannelFutureListener.java:28)
org.asynchttpclient.netty.SimpleChannelFutureListener.operationComplete(SimpleChannelFutureListener.java:20)
io.netty.util.concurrent.DefaultPromise.notifyListener0(DefaultPromise.java:514)
io.netty.util.concurrent.DefaultPromise.notifyListeners0(DefaultPromise.java:507)
io.netty.util.concurrent.DefaultPromise.notifyListenersNow(DefaultPromise.java:486)
io.netty.util.concurrent.DefaultPromise.notifyListeners(DefaultPromise.java:427)
io.netty.util.concurrent.DefaultPromise.tryFailure(DefaultPromise.java:129)
io.netty.channel.nio.AbstractNioChannel.doClose(AbstractNioChannel.java:458)
io.netty.channel.socket.nio.NioSocketChannel.doClose(NioSocketChannel.java:235)
io.netty.channel.AbstractChannel$AbstractUnsafe.doClose0(AbstractChannel.java:632)
io.netty.channel.AbstractChannel$AbstractUnsafe.close(AbstractChannel.java:611)
io.netty.channel.AbstractChannel$AbstractUnsafe.close(AbstractChannel.java:554)
io.netty.channel.nio.NioEventLoop.closeAll(NioEventLoop.java:637)
io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:406)
io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:140)
io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:144)
java.lang.Thread.run(Thread.java:745)

Circe Encoders and Decoders with Http4s

I am trying to use http4s, circe and http4s-circe.
Below I am trying to use the auto derivation feature of circe.
import org.http4s.client.blaze.SimpleHttp1Client
import org.http4s.Status.ResponseClass.Successful
import io.circe.syntax._
import org.http4s._
import org.http4s.headers._
import org.http4s.circe._
import scalaz.concurrent.Task
import io.circe._
final case class Login(username: String, password: String)
final case class Token(token: String)
object JsonHelpers {
import io.circe.generic.auto._
implicit val loginEntityEncoder : EntityEncoder[Login] = jsonEncoderOf[Login]
implicit val loginEntityDecoder : EntityDecoder[Login] = jsonOf[Login]
implicit val tokenEntityEncoder: EntityEncoder[Token] = jsonEncoderOf[Token]
implicit val tokenEntityDecoder : EntityDecoder[Token] = jsonOf[Token]
}
object Http4sTest2 extends App {
import JsonHelpers._
val url = "http://"
val uri = Uri.fromString(url).valueOr(throw _)
val list = List[Header](`Content-Type`(MediaType.`application/json`), `Accept`(MediaType.`application/json`))
val request = Request(uri = uri, method = Method.POST)
.withBody(Login("foo", "bar").asJson)
.map{r => r.replaceAllHeaders(list :_*)}.run
val client = SimpleHttp1Client()
val result = client.fetch[Option[Token]](request){
case Successful(response) => response.as[Token].map(Some(_))
case _ => Task(Option.empty[Token])
}.run
println(result)
}
I get multiple instances of these two compiler errors
Error:scalac: missing or invalid dependency detected while loading class file 'GenericInstances.class'.
Could not access type Secondary in object io.circe.Encoder,
because it (or its dependencies) are missing. Check your build definition for
missing or conflicting dependencies. (Re-run with `-Ylog-classpath` to see the problematic classpath.)
A full rebuild may help if 'GenericInstances.class' was compiled against an incompatible version of io.circe.Encoder.
Error:(25, 74) could not find implicit value for parameter encoder: io.circe.Encoder[Login]
implicit val loginEntityEncoder : EntityEncoder[Login] = jsonEncoderOf[Login]
I was able to solve this. I did a search on google on sbt circe dependency and I copy pasted the first search result. that was circe 0.1 and that is why things were not working for me.
I changed my dependencies to
libraryDependencies ++= Seq(
"org.http4s" %% "http4s-core" % http4sVersion,
"org.http4s" %% "http4s-dsl" % http4sVersion,
"org.http4s" %% "http4s-blaze-client" % http4sVersion,
"org.http4s" %% "http4s-circe" % http4sVersion,
"io.circe" %% "circe-core" % "0.7.0",
"io.circe" %% "circe-generic" % "0.7.0"
)
and now automatic derivation works fine and I am able to compile the code below
import org.http4s.client.blaze.SimpleHttp1Client
import org.http4s._
import org.http4s.headers._
import org.http4s.circe._
import scalaz.concurrent.Task
import io.circe.syntax._
import io.circe.generic.auto._
import org.http4s.Status.ResponseClass.Successful
case class Login(username: String, password: String)
case class Token(token: String)
object JsonHelpers {
implicit val loginEntityEncoder : EntityEncoder[Login] = jsonEncoderOf[Login]
implicit val loginEntityDecoder : EntityDecoder[Login] = jsonOf[Login]
implicit val tokenEntityEncoder: EntityEncoder[Token] = jsonEncoderOf[Token]
implicit val tokenEntityDecoder : EntityDecoder[Token] = jsonOf[Token]
}
object Http4sTest2 extends App {
import JsonHelpers._
val url = "http://"
val uri = Uri.fromString(url).valueOr(throw _)
val list = List[Header](`Content-Type`(MediaType.`application/json`), `Accept`(MediaType.`application/json`))
val request = Request(uri = uri, method = Method.POST)
.withBody(Login("foo", "bar").asJson)
.map{r => r.replaceAllHeaders(list :_*)}.run
val client = SimpleHttp1Client()
val result = client.fetch[Option[Token]](request){
case Successful(response) => response.as[Token].map(Some(_))
case _ => Task(Option.empty[Token])
}.run
println(result)
}

Get content from Akka ResponseEntity in Scala

I do a GET HTTP call to a rest service which returns a json. I would like to parse the json to a scala object but here I got stuck. I am using the Akka api and I can't manage to retrieve the content from the Akka's ResponseEntity
Here is my sbt file:
name := "ScalaHttp"
version := "1.0"
scalaVersion := "2.11.8"
libraryDependencies ++={
val akkaV = "2.4.5"
Seq(
"com.typesafe.akka" %% "akka-http-core" % akkaV,
"com.typesafe.play" %% "play-json" % "2.4.0-M3"
)
}
And here is the app
import akka.actor.ActorSystem
import akka.http.scaladsl.Http
import akka.http.scaladsl.model.HttpRequest
import akka.stream.ActorMaterializer
import scala.concurrent.ExecutionContext.Implicits.global
object Sender {
def main(args: Array[String]): Unit = {
implicit val system = ActorSystem()
implicit val materializer = ActorMaterializer()
Http().singleRequest(HttpRequest(uri = "http://declinators.com/declinator?noun=komunikacja")) foreach {
y => println(y.entity)
println(y.entity.getContentType())
println(y.entity.contentType)
}
}
}
This prints:
HttpEntity.Strict(application/json,{"nominative":"komunikacja","genitive":"komunikacji","dative":"komunikacji","accusative":"komunikację","instrumental":"komunikacją","locative":"komunikacji","vocative":"komunikacjo"})
application/json
application/json
Here come the questions:
1. Why ResponseEntity supplies getContentType() and contentType()? They return the same thing.
2. Getting the contentyType is easy, you have two ways for doing it, but how can I get the content itself, so I can play (i.e. parse it using play) with the json!
You can use entity.data.toString for Binary content type, or the following code piece
data.decodeString(nb.charset.value)
Please follow the HttpEntity.Strict.toString implementation here for detail:
https://github.com/akka/akka/blob/master/akka-http-core/src/main/scala/akka/http/scaladsl/model/HttpEntity.scala#L316

Sending post with json using spray?

Sorry I can't manage to make this work: I need to add some json to a post, so following the documentation: http://spray.io/documentation/1.1-M8/spray-httpx/request-building/ :
import scala.util.{Success, Failure}
import akka.actor.{Props, ActorSystem}
import spray.can.client.DefaultHttpClient
import spray.client.HttpConduit
import spray.httpx.SprayJsonSupport
import spray.http._
import spray.json.JsonParser._
import spray.json._
import HttpMethods._
import HttpHeaders._
import MediaTypes._
import spray.httpx.RequestBuilding._
import scala.concurrent.ExecutionContext.Implicits.global
...
val req = HttpRequest(method = POST, uri = "/api/1.0/users/ping.json", entity = HttpEntity(`application/json`,"""{ "key"="whatever" }"""))
and it never compiles:
overloaded method value apply with alternatives:
[error] (optionalBody: Option[spray.http.HttpBody])spray.http.HttpEntity <and>
[error] (buffer: Array[Byte])spray.http.HttpEntity <and>
[error] (string: String)spray.http.HttpEntity
[error] cannot be applied to (spray.http.MediaType, String)
[error] val req = HttpRequest(method = POST, uri = "/api/1.0/users/ping.json", entity = HttpEntity(`application/json`,"""{ "key"="whatever"}"""))
Had the same problem and found the solution here:
https://github.com/spray/spray-json/blob/master/src/main/scala/spray/json/AdditionalFormats.scala#L30-41
This finally worked for me:
import spray.httpx.SprayJsonSupport
import spray.json.AdditionalFormats
object Client extends SprayJsonSupport with AdditionalFormats {
val email = "..."
val password = "..."
val pipeline = sendReceive
pipeline(Post("http://something.com/login", s"""{
"email": "$email",
"password": "$password"
}""".asJson.asJsObject))
}
Sorry, but you question is a bit cumbersome, for me at least. If you want to make a POST request in Spray with some Json as HttpEntity, then you should try to do it with Spray-Clients pipelining, it's drop dead simple.
You need to create a simple pipe:
val pipe: HttpRequest => Future[HttpResponse] = sendReceive
and then build a request:
import spray.json.SprayJsonSupport._
pipe(Post("/api/1.0/users/ping", """{ "key"="whatever" }""".asJson))
This will return you a Future with HttpResponse, if you want some specific result, let's say, for example, some confirmation code, then add unmarshall step to your pipe:
val pipe: HttpRequest => Future[ConfCode] = sendReceive ~> unmarshal[ConfCode]
That is what i tried too, but it doesn't work it is telling me that I am missing an implicit:
val pipeline = sendReceive(conduit)
val responseF = pipeline(Post("/api/1.0/users/ping.json", """{ "key": "whatever" }""".asJson))
responseF onComplete { ...
but i always get :
could not find implicit value for evidence parameter of type spray.httpx.marshalling.Marshaller[spray.json.JsValue]
[error] val responseF = pipeline(Post("/api/1.0/users/ping.json", """{ "key": "whatever" }""".asJson))
also the import you have in your previous snapshot does not work.... Am i using a bad version of the library? My sbt settings are:
val sprayVersion = "1.1-M7"
val akkaVersion = "2.1.1"
"io.spray" %% "spray-json" % "1.2.5",
"com.typesafe.akka" %% "akka-actor" % akkaVersion,
"com.typesafe.akka" %% "akka-slf4j" % akkaVersion,
"io.spray" % "spray-client" % sprayVersion,