Last active
August 29, 2015 14:17
-
-
Save wmeints/e366ff5e4ffb1a3fd1f8 to your computer and use it in GitHub Desktop.
Extensions to make handling request in Spray framework easier to do
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package nl.fizzylogic.restservice | |
import akka.actor.{Actor, ActorRef, Props, ReceiveTimeout} | |
import org.json4s.DefaultFormats | |
import spray.httpx.Json4sSupport | |
import spray.http.{StatusCode, StatusCodes} | |
import spray.routing.{HttpService, RequestContext} | |
import scala.concurrent.duration._ | |
import scala.reflect.ClassTag | |
case class GenericError(message:String) | |
case class ObjectNotFound(data: AnyRef) | |
case object ObjectRemoved | |
object RequestHandler { | |
case class CreateRequestActor[S <: AnyRef](requestContext: RequestContext, | |
target: ActorRef, | |
message: AnyRef)(implicit tag: ClassTag[S]) extends RequestHandler(requestContext, target, message) { | |
override def processResult: Receive = { | |
case tag(data) => complete(StatusCodes.Created, data) | |
} | |
} | |
case class InvokeRequestActor[S <: AnyRef](requestContext: RequestContext, | |
target: ActorRef, | |
message: AnyRef)(implicit tag: ClassTag[S]) extends RequestHandler(requestContext, target, message) { | |
override def processResult: Receive = { | |
case tag(data) => complete(StatusCodes.OK, data) | |
} | |
} | |
case class UpdateRequestActor[S <: AnyRef](requestContext: RequestContext, | |
target: ActorRef, | |
message: AnyRef)(implicit tag: ClassTag[S]) extends RequestHandler(requestContext, target, message) { | |
override def processResult: Receive = { | |
case tag(data) => complete(StatusCodes.OK, data) | |
case ObjectNotFound(error) => complete(StatusCodes.NotFound, error) | |
} | |
} | |
case class GetRequestActor[S <: AnyRef](requestContext: RequestContext, | |
target: ActorRef, | |
message: AnyRef)(implicit tag: ClassTag[S]) extends RequestHandler(requestContext, target, message) { | |
override def processResult: Receive = { | |
case tag(data) => complete(StatusCodes.OK, data) | |
case ObjectNotFound(error) => complete(StatusCodes.NotFound, error) | |
} | |
} | |
case class DeleteRequestActor(requestContext: RequestContext, | |
target: ActorRef, | |
message: AnyRef) extends RequestHandler(requestContext, target, message) { | |
override def processResult: Receive = { | |
case ObjectRemoved => complete(StatusCodes.NoContent, "") | |
case ObjectNotFound(error) => complete(StatusCodes.NotFound, error) | |
} | |
} | |
} | |
abstract class RequestHandler(requestContext: RequestContext, target: ActorRef, message: AnyRef) extends Actor with Json4sSupport { | |
val json4sFormats = DefaultFormats | |
def processResult: Receive | |
// Set the timeout to 5 seconds and | |
// pass the original message on to the target actor | |
// When no response is received within those 5 seconds, the default receive handler | |
// will complete the request with an error. | |
context.setReceiveTimeout(5.seconds) | |
target ! message | |
// Completes the request with the response from the target actor. | |
def complete[T <: AnyRef](status: StatusCode, obj: T) = { | |
requestContext.complete(status, obj) | |
context.stop(self) | |
} | |
def notFound() = { | |
requestContext.complete(StatusCodes.NotFound, GenericError("The specified entity does not exist")) | |
context.stop(self) | |
} | |
// First try the process result functionality. | |
// When that doesn't give a result, use the default receive to sort things out. | |
override def receive = processResult orElse defaultReceive | |
def defaultReceive: Receive = { | |
case ReceiveTimeout => | |
complete(StatusCodes.GatewayTimeout, GenericError("request timeout")) | |
case res => | |
complete(StatusCodes.InternalServerError, GenericError("Something unexpected happened")) | |
} | |
} | |
trait HandlerPerRequest { | |
self: HttpService => | |
import nl.fizzylogic.restservice.route.RequestHandler._ | |
def handleCreate[S <: AnyRef](requestContext: RequestContext, target: ActorRef, message: AnyRef)(implicit tag: ClassTag[S]) = { | |
actorRefFactory.actorOf(Props(CreateRequestActor[S](requestContext, target, message))) | |
} | |
def handleUpdate[S <: AnyRef](requestContext: RequestContext, target: ActorRef, message: AnyRef)(implicit tag: ClassTag[S]) = { | |
actorRefFactory.actorOf(Props(UpdateRequestActor[S](requestContext, target, message))) | |
} | |
def handleDelete(requestContext: RequestContext, target: ActorRef, message: AnyRef) = { | |
actorRefFactory.actorOf(Props(DeleteRequestActor(requestContext, target, message))) | |
} | |
def handleInvoke[S <: AnyRef](requestContext: RequestContext, target: ActorRef, message: AnyRef)(implicit tag: ClassTag[S]) = { | |
actorRefFactory.actorOf(Props(InvokeRequestActor[S](requestContext, target, message))) | |
} | |
def handleGet[S <: AnyRef](requestContext: RequestContext, target: ActorRef, message: AnyRef)(implicit tag: ClassTag[S]) = { | |
actorRefFactory.actorOf(Props(GetRequestActor[S](requestContext, target, message))) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment