Joel
Joel

Reputation: 689

Error while doing a very basic test of http server using scala akka spray can

I did a very basic test of spray can using:

Here is my code:

val myListener: ActorRef = system.actorOf(Props[TestHttpListener], "httpListener")
IO(Http) ! Http.Bind(myListener, interface = "localhost", port = 8080)

The httpListener reacts to Http.Connected with Http.Register(self).

I use sbt to run my code. It fails with an AbstractMethodError:

[ERROR] [07/12/2014 18:46:48.364] [default-akka.actor.default-dispatcher-5] [ActorSystem(default)] Uncaught error from thread [default-akka.actor.default-dispatcher-5] shutting down JVM since 'akka.jvm-exit-on-fatal-error' is enabled
java.lang.AbstractMethodError: spray.can.HttpManager.akka$actor$ActorLogging$_setter_$log_$eq(Lakka/event/LoggingAdapter;)V
        at akka.actor.ActorLogging$class.$init$(Actor.scala:335)
        at spray.can.HttpManager.<init>(HttpManager.scala:29)
        at spray.can.HttpExt$$anonfun$1.apply(Http.scala:153)
        at spray.can.HttpExt$$anonfun$1.apply(Http.scala:153)
        at akka.actor.TypedCreatorFunctionConsumer.produce(Props.scala:422)
        at akka.actor.Props.newActor(Props.scala:331)
        at akka.actor.ActorCell.newActor(ActorCell.scala:534)
        at akka.actor.ActorCell.create(ActorCell.scala:560)
        at akka.actor.dungeon.FaultHandling$class.finishCreate(FaultHandling.scala:135)
        at akka.actor.dungeon.FaultHandling$class.faultCreate(FaultHandling.scala:129)
        at akka.actor.ActorCell.faultCreate(ActorCell.scala:338)
        at akka.actor.dungeon.FaultHandling$class.faultRecreate(FaultHandling.scala:58)
        at akka.actor.ActorCell.faultRecreate(ActorCell.scala:338)
        at akka.actor.ActorCell.invokeAll$1(ActorCell.scala:428)
        at akka.actor.ActorCell.systemInvoke(ActorCell.scala:447)
        at akka.dispatch.Mailbox.processAllSystemMessages(Mailbox.scala:262)
        at akka.dispatch.Mailbox.run(Mailbox.scala:218)
        at akka.dispatch.ForkJoinExecutorConfigurator$AkkaForkJoinTask.exec(AbstractDispatcher.scala:385)
        at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260)
        at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339)
        at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979)
        at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107)

Upvotes: 4

Views: 2515

Answers (1)

Sotomajor
Sotomajor

Reputation: 1386

In Scala world it's very important to be careful with versions since everything develops rapidly and backwards compatibility between major versions is not guaranteed.

Spray depends on Akka and this page contains information regarding supported combinations.

spray 1.3.1 is built against Scala 2.10.3 and Akka 2.3.0 as well as Scala 2.11.1 and Akka 2.3.2.

So, if you use Scala 2.10.3 the correct version of Akka would be at least 2.3.0.

Moreover, the example which I think you are following from this page is not very good.

First of all it misses the fact that the code where you initialise the thing with IO(Http) ! Http.Bind(...) must be inside an actor. The actor, which received Http.Bind tries to reply with Http.Bound and since the caller is not an actor you get dead letters.

So, I would advise to do something like this:

class MyApp extends Actor {

  implicit val system = context.system

  override def receive: Receive = {
    case "start" =>
      val myListener: ActorRef = system.actorOf(Props[TestHttpListener], "httpListener")
      IO(Http) ! Http.Bind(myListener, interface = "localhost", port = 8080)
  }
}

Then from the main() method of your app you need to do:

val myApp: ActorRef = system.actorOf(Props[MyApp], "myApp")
myApp ! "start"

Another thing that is not so easy to understand from the example is that in your listener actor you need not only to process messages, but also register itself on every connection:

class TestHttpListener extends Actor {
  def receive = {
    case HttpRequest(HttpMethods.GET, Uri.Path("/ping"), _, _, _) =>
      sender() ! HttpResponse(entity = "PONG")

    case c : Tcp.Connected =>
      sender() ! Http.Register(self)
  }
}

Upvotes: 5

Related Questions