Skip to content

Instantly share code, notes, and snippets.

@wmeints
Last active August 29, 2015 14:17
Show Gist options
  • Save wmeints/e366ff5e4ffb1a3fd1f8 to your computer and use it in GitHub Desktop.
Save wmeints/e366ff5e4ffb1a3fd1f8 to your computer and use it in GitHub Desktop.
Extensions to make handling request in Spray framework easier to do
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