I am new one in akka spray :)
I have this router actor:
class ProjectResource extends HttpServiceActor with DefaultJsonFormats {
import spray.http.MediaTypes.`application/json`
def receive = runRoute {
pathPrefix("rest" / "1.2") {
path("users" / Segment / "projects" / Segment / "auth_url") {
case (key, id) =>
get {
respondWithMediaType(`application/json`) {
requestContext =>
val responder = context actorOf Responder.props(sender(), requestContext)
context actorOf GetTask.props(key, id, responder)
}
}
}
}
}
}
and responder:
class Responder(replyTo: ActorRef, ctx: RequestContext) extends Actor with DefaultJsonFormats {
implicit val authFormat = jsonFormat2(AuthDTO)
def receive = {
case x: AuthDTO =>
ctx.complete(200, x)
context stop self
}
}
and when i tried to
curl -v http://localhost:8080/rest/1.2/users/123/projects/1/auth_url
i got this error (on ctx.complete(200, x) string ...):
[ERROR] [07/11/2016 18:40:32.954] [default-akka.actor.default-dispatcher-3] [akka://default/user/projectResource] Error during processing of request HttpRequest(GET,http://localhost:8080/rest/1.2/users/123/projects/1/auth_url,List(User-Agent: curl/7.35.0, Host: localhost:8080),Empty,HTTP/1.1)
java.lang.IllegalArgumentException: requirement failed
What i am doing wrong? Help Please!
Related
The following code example (which you can copy and run) shows a MyParentActor that creates a MyChildActor.
The MyChildActor throws an exception for its first message which causes it to be restarted.
However, what I want to achieve is for "Message 1" to still be processed before "Message 2" on restart of the MyChildActor.
Instead, what is happening is that Message 1 is added to the tail of the mailbox queue, and so Message 2 is processed first.
How do I achieve ordering of the original messages on restart of an actor, without having to create my own mailbox etc?
object TestApp extends App {
var count = 0
val actorSystem = ActorSystem()
val parentActor = actorSystem.actorOf(Props(classOf[MyParentActor]))
parentActor ! "Message 1"
parentActor ! "Message 2"
class MyParentActor extends Actor with ActorLogging{
var childActor: ActorRef = null
#throws[Exception](classOf[Exception])
override def preStart(): Unit = {
childActor = context.actorOf(Props(classOf[MyChildActor]))
}
override def receive = {
case message: Any => {
childActor ! message
}
}
override def supervisorStrategy: SupervisorStrategy = {
OneForOneStrategy() {
case _: CustomException => Restart
case _: Exception => Restart
}
}
}
class MyChildActor extends Actor with ActorLogging{
override def preRestart(reason: Throwable, message: Option[Any]): Unit = {
message match {
case Some(e) => self ! e
}
}
override def receive = {
case message: String => {
if (count == 0) {
count += 1
throw new CustomException("Exception occurred")
}
log.info("Received message {}", message)
}
}
}
class CustomException(message: String) extends RuntimeException(message)
}
You could mark the failing message with a special envelope and stash everything up to the receiving of that message (see child actor implementation). Just define a behaviour where the actor stashes every message except for the specific envelope, processes it's payload and then unstashes all other messages and returns to it's normal behaviour.
This gives me:
INFO TestApp$MyChildActor - Received message Message 1
INFO TestApp$MyChildActor - Received message Message 2
object TestApp extends App {
var count = 0
val actorSystem = ActorSystem()
val parentActor = actorSystem.actorOf(Props(classOf[MyParentActor]))
parentActor ! "Message 1"
parentActor ! "Message 2"
class MyParentActor extends Actor with ActorLogging{
var childActor: ActorRef = null
#throws[Exception](classOf[Exception])
override def preStart(): Unit = {
childActor = context.actorOf(Props(classOf[MyChildActor]))
}
override def receive = {
case message: Any => {
childActor ! message
}
}
override def supervisorStrategy: SupervisorStrategy = {
OneForOneStrategy() {
case e: CustomException => Restart
case _: Exception => Restart
}
}
}
class MyChildActor extends Actor with Stash with ActorLogging{
override def preRestart(reason: Throwable, message: Option[Any]): Unit = {
message match {
case Some(e) =>
self ! Unstash(e)
}
}
override def postRestart(reason: Throwable): Unit = {
context.become(stashing)
preStart()
}
override def receive = {
case message: String => {
if (count == 0) {
count += 1
throw new CustomException("Exception occurred")
}
log.info("Received message {}", message)
}
}
private def stashing: Receive = {
case Unstash( payload ) =>
receive(payload)
unstashAll()
context.unbecome()
case m =>
stash()
}
}
case class Unstash( payload: Any )
class CustomException(message: String) extends RuntimeException(message)
}
I want to configure spray http client in a way that controls max number of request that were sent to the server. I need this because server that i'm sending requests to blocks me if there are more then 2 request were sent. I get
akka.pattern.AskTimeoutException: Ask timed out on [Actor[akka://smallTasks/user/IO-HTTP#151444590]] after [15000 ms]
akka.pattern.AskTimeoutException: Ask timed out on [Actor[akka://smallTasks/user/IO-HTTP#151444590]] after [15000 ms]
akka.pattern.AskTimeoutException: Ask timed out on [Actor[akka://smallTasks/user/IO-HTTP#151444590]] after [15000 ms]
akka.pattern.AskTimeoutException: Ask timed out on
I need to send thousands of requests but i get blocked after i got responses from ~ 100 requests.
I have this method:
implicit val system = ActorSystem("smallTasks")
implicit val timeout = new Timeout(15.seconds)
import system.dispatcher
def doHttpRequest(url: String): Future[HttpResponse] = {
(IO(Http) ? HttpRequest(GET, Uri(url))).mapTo[HttpResponse]
}
And here i catch responses and retry if it fails(recursively):
def getOnlineOffers(modelId: Int, count: Int = 0): Future[Any] = {
val result = Promise[Any]()
AkkaSys.doHttpRequest(Market.modelOffersUrl(modelId)).map(response => {
val responseCode = response.status.intValue
if (List(400, 404).contains(responseCode)) {
result.success("Bad request")
} else if (responseCode == 200) {
Try {
Json.parse(response.entity.asString).asOpt[JsObject]
} match {
case Success(Some(obj)) =>
Try {
(obj \\ "onlineOffers").head.as[Int]
} match {
case Success(offers) => result.success(offers)
case _ => result.success("Can't find property")
}
case _ => result.success("Wrong body")
}
} else {
result.success("Unexpected error")
}
}).recover { case err =>
if (count > 5) {
result.success("Too many tries")
} else {
println(err.toString)
Thread.sleep(200)
getOnlineOffers(modelId, count + 1).map(r => result.success(r))
}
}
result.future
}
How to do this properly? May be i need to configure akka dispatcher somehow?
you can use http://spray.io/documentation/1.2.2/spray-client/ and write you personal pipeline
val pipeline: Future[SendReceive] =
for (
Http.HostConnectorInfo(connector, _) <-
IO(Http) ? Http.HostConnectorSetup("www.spray.io", port = 80)
) yield sendReceive(connector)
val request = Get("/segment1/segment2/...")
val responseFuture: Future[HttpResponse] = pipeline.flatMap(_(request))
to get HttpResponse
import scala.concurrent.Await
import scala.concurrent.duration._
val response: HttpResponse = Aweit(responseFuture, ...)
to convert
import spray.json._
response.entity.asString.parseJson.convertTo[T]
to check
Try(response.entity.asString.parseJson).isSuccess
too many brackets. In scala you can write it shorter
Actually I`m having trouble with getting my actor (router) system to work correctly.
My Setup:
I`m trying to use an akka router within an play controller. For dependency injection I use scaldi.
scaldi module:
class UserDAOModule extends Module {
binding to new ExampleRouter
binding toProvider new UserDAOWorker
}
akka router:
class UserDAORouter(implicit inj:Injector) extends Actor with AkkaInjectable {
val userDAOProps = injectActorProps[UserDAOWorker]
var router = {
val routees = Vector.fill(5) {
val r = context.actorOf(userDAOProps)
context watch r
ActorRefRoutee(r)
}
Router(RoundRobinRoutingLogic(), routees)
}
override def receive: Receive = {
case mm: MongoDBMessage =>
router.route(mm, sender)
case Terminated(a) =>
router = router.removeRoutee(a)
val r = context.actorOf(userDAOProps)
context watch r
router = router.addRoutee(r)
}
}
worker:
class UserDAOWorker(implicit inj:Injector) extends Actor with Injectable {
val db = inject[DefaultDB]
val collection:JSONCollection = db("users")
val currentSender = sender
override def receive: Receive = {
case InsertUser(user) => insertUser(user)
}
def insertUser(user:User) = {
collection.save(user).onComplete {
case Failure(e) => currentSender ! new UserDAOReturnMessage(Some(e), None)
case Success(lastError) => currentSender ! new UserDAOReturnMessage(None, lastError)
}
}
}
When I send a message (insertUser message) to the router, it is routed correctly and the worker receives the message, but when the worker sends a message back to the sender it cant be delivered, so it is send to dead letter office. I cant figure out how to fix this. Is there someone able to help me?
Thanks in advance
I guess the problem is that the currentSender is initialized with null (i.e. ActorRef.noSender) in the constructor on actor creation. 'sender' is only valid in context of receiving a message in receive(). Sending a message to ActorRef.noSender is an equivalent of sending to dead letter queue.
Something like this should work:
class UserDAOWorker(implicit inj:Injector) extends Actor with Injectable {
val db = inject[DefaultDB]
val collection:JSONCollection = db("users")
override def receive: Receive = {
case InsertUser(user) => {
insertUser(sender, user)
}
}
def insertUser(currentSender : ActorRef, user:User) = {
collection.save(user).onComplete {
case Failure(e) => currentSender ! new UserDAOReturnMessage(Some(e), None)
case Success(lastError) => currentSender ! new UserDAOReturnMessage(None, lastError)
}
}
}
i have a next code
class MySystem(outerResourse: OuterResourse) extends Actor {
val firstActor = context.actorOf(Props(new FirstActor(outerResourse)), "first")
val secondActor = context.actorOf(Props(new SecondActor(firstActor)), "second")
secondActor ! Go
def receive = {
case x: AnotherMessage => printl(s"another message: $x")
case x => println(x)
}
}
class FirstActor(outerResourse: OuterResourse) extends Actor {
def receive = {
case Test =>
context.parent ! AnotherMessage
sender ! "ok"
}
}
class SecondActor(firstActor: ActorRef) extends Actor {
def receive = {
case Go => firstActor ! Test
case "ok" => println("ok")
}
}
Here OuterResourse is a any resourse - file, internet connection...
i would like to check a behavior, but i in embarrassment, i do not know how to check that second actor will be got a "ok", and mySystem actor will be got a AnotherMessage
class MyTest(_system: ActorSystem) extends TestKit(_system)
with ImplicitSender with FunSpecLike with Matchers {
def this() = this(ActorSystem("myTest"))
val outerResourse = new OuterResourse()
val mySystem = system.actorOf(Props(new MySytem(outerResourse)))
describe("Actors") {
it("should get AnotherMessage message and ok message") {
???
}
}
}
Hot to check, that a secondActor got a "ok" message?
You could set custom OutputStream for println usnig Console.withOut. It could be mock and "wait" for OK message from first actor. But it is not very nice....
// edit: please read documentation http://doc.akka.io/docs/akka/snapshot/scala/testing.html
Akka provide they owm testing "framework"
I have the next code:
//TestActor got some message
class TestActor extends Actor {
def receive = {
case string: String => //....
}
}
//TestReg when create get ActorRef, when i call `pass` method, then should pass text to ActorRef
class TestReg(val actorRef: ActorRef) {
def pass(text: String) {
actorRef ! text
}
}
When i wrote test:
class TestActorReg extends TestKit(ActorSystem("system")) with ImplicitSender
with FlatSpecLike with MustMatchers with BeforeAndAfterAll {
override def afterAll() {
system.shutdown()
}
"actorReg" should "pass text to actorRef" in {
val probe = TestProbe()
val testActor = system.actorOf(Props[TestActor])
probe watch testActor
val testReg = new TestReg(testActor)
testReg.pass("test")
probe.expectMsg("test")
}
}
I got error:
java.lang.AssertionError: assertion failed: timeout (3 seconds) during expectMsg while waiting for test
How to check what the actor got a text?
probe.expectMsg() is calling the assertion on the probe. But you passed the testActor into your TestReg class
change it to the following line and it will work
val testReg = new TestReg(probe.ref)
have to call .ref to make the probe into an ActorRef
And you want to do it here not at the instantiation of the variable to avoid
certain bugs that are outside of the scope of this response
the error in the logic as I see it is you are thinking that the watch method makes probe see what test actor does. but its death watch not message watch. which is different.
Create application.conf file with this:
akka {
test {
timefactor = 1.0
filter-leeway = 999s
single-expect-default = 999s
default-timeout = 999s
calling-thread-dispatcher {
type = akka.testkit.CallingThreadDispatcherConfigurator
}
}
actor {
serializers {
test-message-serializer = "akka.testkit.TestMessageSerializer"
}
serialization-identifiers {
"akka.testkit.TestMessageSerializer" = 23
}
serialization-bindings {
"akka.testkit.JavaSerializable" = java
}
}
}