Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spray/Akka missing implicit

Tags:

scala

akka

spray

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...

like image 503
matanster Avatar asked Jun 18 '14 11:06

matanster


2 Answers

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.

like image 73
cfchou Avatar answered Sep 21 '22 00:09

cfchou


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)
        }
      }
    }
  }
}
like image 21
Christian Avatar answered Sep 19 '22 00:09

Christian