Spray/Akka missing implicit
Asked Answered
R

2

7

MyService.scala:33: could not find implicit value for parameter eh: spray.routing.ExceptionHandler

I have run into a "missing implicit" compilation error using Akka, in spray.io code that makes an http call to a separate back-end server, as part of responding to an http get. The code needs to import quite a lot of Spray and Akka libraries, so it's a bit hard figuring whether there may be some library conflicts causing this, and I'd rather figure how to logically trace this sort of problem for this and other cases.

The missing implicit is encountered on calling runRoute(myRoute)

Here's the code:

import spray.routing._
import akka.actor.Actor
import akka.actor.ActorSystem
import spray.http._
import MediaTypes._
import akka.io.IO
import spray.httpx.RequestBuilding._
import scala.concurrent.Future
import spray.can.Http
import spray.http._
import akka.util.Timeout
import HttpMethods._
import akka.pattern.ask
import akka.event.Logging
import scala.concurrent.duration._


// we don't implement our route structure directly in the service actor because
// we want to be able to test it independently, without having to spin up an actor
class MyServiceActor extends Actor with MyService with akka.actor.ActorLogging {

  log.info("Starting")

  // the HttpService trait defines only one abstract member, which
  // connects the services environment to the enclosing actor or test
  def actorRefFactory = context

  // this actor only runs our route, but you could add
  // other things here, like request stream processing
  // or timeout handling
  def receive = runRoute(myRoute)
}

// this trait defines our service behavior independently from the service actor
trait MyService extends HttpService {

  implicit val system: ActorSystem = ActorSystem()

  implicit val timeout: Timeout = Timeout(15.seconds)

  import system.dispatcher // implicit execution context

  //val logger = context.actorSelection("/user/logger")
  val logger = actorRefFactory.actorSelection("../logger")

  val myRoute =
  {
    def forward(): String = {
      logger ! Log("forwarding to backend")  
      val response: Future[HttpResponse] =
      (IO(Http) ? Get("http:3080//localhost/backend")).mapTo[HttpResponse]    
      "<html><body><h1>api response after backend processing</h1></body></html>"
    }

    path("") {
      get {
        respondWithMediaType(`text/html`) { // XML is marshalled to `text/xml` by default, so we simply override here
          complete(forward)
        }
      }
    }
  }
}

I am wondering what's the best way to solve this, hopefully providing insight into how to solve similar problems with implicits being missing, as they are somehow inherently not straightforward to track down.

EDIT: when trying to directly pass implicits as in @christian's answer below, I get:

MyService.scala:35: ambiguous implicit values: both value context in trait Actor of type => akka.actor.ActorContext and value system in trait MyService of type => akka.actor.ActorSystem match expected type akka.actor.ActorRefFactory
RoutingSettings.default, LoggingContext.fromActorRefFactory) ^

Not quite sure why being specific as in @christian's answer leaves room for ambiguity for the compiler...

Rheometer answered 18/6, 2014 at 11:42 Comment(2)
What's the compiler error?Orlena
First guess, you are missing a marshaller.Almanac
O
2

runRoute expects a few implicits. You are missing an import:

import spray.routing.RejectionHandler.Default

Update: I think we also did have some problems with runRoute because we are supplying the implicit parameters explicitly:

runRoute(route)(ExceptionHandler.default, RejectionHandler.Default, context,
      RoutingSettings.default, LoggingContext.fromActorRefFactory)

Update2: To fix the last error, remove the creation of the ActorSystem (in MyService you get the actor system from MyServiceActor - therefore you have to use a self type annotation). This compiles:

import akka.actor.Actor
import akka.io.IO
import spray.httpx.RequestBuilding._
import spray.http.MediaTypes._
import spray.routing.{RoutingSettings, RejectionHandler, ExceptionHandler, HttpService}
import spray.util.LoggingContext
import scala.concurrent.Future
import spray.can.Http
import spray.http._
import akka.util.Timeout
import HttpMethods._
import akka.pattern.ask
import akka.event.Logging
import scala.concurrent.duration._


// we don't implement our route structure directly in the service actor because
// we want to be able to test it independently, without having to spin up an actor
class MyServiceActor extends Actor with MyService with akka.actor.ActorLogging {

  log.info("Starting")

  // the HttpService trait defines only one abstract member, which
  // connects the services environment to the enclosing actor or test
  implicit def actorRefFactory = context

  // this actor only runs our route, but you could add
  // other things here, like request stream processing
  // or timeout handling
  def receive = runRoute(myRoute)(ExceptionHandler.default, RejectionHandler.Default, context,
    RoutingSettings.default, LoggingContext.fromActorRefFactory)
}

// this trait defines our service behavior independently from the service actor
trait MyService extends HttpService { this: MyServiceActor =>

  implicit val timeout: Timeout = Timeout(15.seconds)

  implicit val system = context.system

  //val logger = context.actorSelection("/user/logger")
  val logger = actorRefFactory.actorSelection("../logger")

  val myRoute =
  {
    def forward(): String = {
      //logger ! Log("forwarding to backend")
      val response: Future[HttpResponse] =
        (IO(Http) ? Get("http:3080//localhost/backend")).mapTo[HttpResponse]
      "<html><body><h1>api response after backend processing</h1></body></html>"
    }

    path("") {
      get {
        respondWithMediaType(`text/html`) { // XML is marshalled to `text/xml` by default, so we simply override here
          complete(forward)
        }
      }
    }
  }
}
Orlena answered 18/6, 2014 at 11:53 Comment(6)
I'm afraid this import doesn't change the error message. I think the problem is something else, but it's a bit dodgy tracing this sort of problem.Rheometer
If you aren't going to use the default handler the suggested way is to create an exception handling trait: spray.io/documentation/1.1-SNAPSHOT/spray-routing/key-concepts/… This is how I do it.Adkinson
I'd gladly use any default, not sure how.Rheometer
In my answer you see how to use the default.Orlena
Thanks @Orlena somehow I erred in thinking that didn't work before (maybe before the edit). Indeed this change reveals conflicts, which I can understand, but not sure why specifying the defaults doesn't just overide them [Added the new error to the question as an update]Rheometer
Thanks @Christian! I didn't realize a trait can declare its this context.. boy was this a hard mounting with AkkaRheometer
S
20

I ran into the same "could not find implicit value for parameter eh: spray.routing.ExceptionHandler" error earlier today. I tried @Christian's approach but saw a few "implicit values for xxx" creeping up. After scouting the error message a little I found adding implicit val system = context.system to the actor that runRoute solved the problem.

Skelton answered 29/12, 2014 at 5:38 Comment(1)
Yes, this is all I needed also.Regulable
O
2

runRoute expects a few implicits. You are missing an import:

import spray.routing.RejectionHandler.Default

Update: I think we also did have some problems with runRoute because we are supplying the implicit parameters explicitly:

runRoute(route)(ExceptionHandler.default, RejectionHandler.Default, context,
      RoutingSettings.default, LoggingContext.fromActorRefFactory)

Update2: To fix the last error, remove the creation of the ActorSystem (in MyService you get the actor system from MyServiceActor - therefore you have to use a self type annotation). This compiles:

import akka.actor.Actor
import akka.io.IO
import spray.httpx.RequestBuilding._
import spray.http.MediaTypes._
import spray.routing.{RoutingSettings, RejectionHandler, ExceptionHandler, HttpService}
import spray.util.LoggingContext
import scala.concurrent.Future
import spray.can.Http
import spray.http._
import akka.util.Timeout
import HttpMethods._
import akka.pattern.ask
import akka.event.Logging
import scala.concurrent.duration._


// we don't implement our route structure directly in the service actor because
// we want to be able to test it independently, without having to spin up an actor
class MyServiceActor extends Actor with MyService with akka.actor.ActorLogging {

  log.info("Starting")

  // the HttpService trait defines only one abstract member, which
  // connects the services environment to the enclosing actor or test
  implicit def actorRefFactory = context

  // this actor only runs our route, but you could add
  // other things here, like request stream processing
  // or timeout handling
  def receive = runRoute(myRoute)(ExceptionHandler.default, RejectionHandler.Default, context,
    RoutingSettings.default, LoggingContext.fromActorRefFactory)
}

// this trait defines our service behavior independently from the service actor
trait MyService extends HttpService { this: MyServiceActor =>

  implicit val timeout: Timeout = Timeout(15.seconds)

  implicit val system = context.system

  //val logger = context.actorSelection("/user/logger")
  val logger = actorRefFactory.actorSelection("../logger")

  val myRoute =
  {
    def forward(): String = {
      //logger ! Log("forwarding to backend")
      val response: Future[HttpResponse] =
        (IO(Http) ? Get("http:3080//localhost/backend")).mapTo[HttpResponse]
      "<html><body><h1>api response after backend processing</h1></body></html>"
    }

    path("") {
      get {
        respondWithMediaType(`text/html`) { // XML is marshalled to `text/xml` by default, so we simply override here
          complete(forward)
        }
      }
    }
  }
}
Orlena answered 18/6, 2014 at 11:53 Comment(6)
I'm afraid this import doesn't change the error message. I think the problem is something else, but it's a bit dodgy tracing this sort of problem.Rheometer
If you aren't going to use the default handler the suggested way is to create an exception handling trait: spray.io/documentation/1.1-SNAPSHOT/spray-routing/key-concepts/… This is how I do it.Adkinson
I'd gladly use any default, not sure how.Rheometer
In my answer you see how to use the default.Orlena
Thanks @Orlena somehow I erred in thinking that didn't work before (maybe before the edit). Indeed this change reveals conflicts, which I can understand, but not sure why specifying the defaults doesn't just overide them [Added the new error to the question as an update]Rheometer
Thanks @Christian! I didn't realize a trait can declare its this context.. boy was this a hard mounting with AkkaRheometer

© 2022 - 2024 — McMap. All rights reserved.