Statistics
| Branch: | Tag: | Revision:

root / logic / src / main / scala / gr / grnet / aquarium / rest / actor / RESTActor.scala @ d305617f

History | View | Annotate | Download (5.8 kB)

1 4dcd61e8 Christos KK Loverdos
package gr.grnet.aquarium.rest.actor
2 4dcd61e8 Christos KK Loverdos
3 a98e1a6e Christos KK Loverdos
/*
4 a98e1a6e Christos KK Loverdos
 * Copyright 2011 GRNET S.A. All rights reserved.
5 a98e1a6e Christos KK Loverdos
 *
6 a98e1a6e Christos KK Loverdos
 * Redistribution and use in source and binary forms, with or
7 a98e1a6e Christos KK Loverdos
 * without modification, are permitted provided that the following
8 a98e1a6e Christos KK Loverdos
 * conditions are met:
9 a98e1a6e Christos KK Loverdos
 *
10 a98e1a6e Christos KK Loverdos
 *   1. Redistributions of source code must retain the above
11 a98e1a6e Christos KK Loverdos
 *      copyright notice, this list of conditions and the following
12 a98e1a6e Christos KK Loverdos
 *      disclaimer.
13 a98e1a6e Christos KK Loverdos
 *
14 a98e1a6e Christos KK Loverdos
 *   2. Redistributions in binary form must reproduce the above
15 a98e1a6e Christos KK Loverdos
 *      copyright notice, this list of conditions and the following
16 a98e1a6e Christos KK Loverdos
 *      disclaimer in the documentation and/or other materials
17 a98e1a6e Christos KK Loverdos
 *      provided with the distribution.
18 a98e1a6e Christos KK Loverdos
 *
19 a98e1a6e Christos KK Loverdos
 * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
20 a98e1a6e Christos KK Loverdos
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 a98e1a6e Christos KK Loverdos
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 a98e1a6e Christos KK Loverdos
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
23 a98e1a6e Christos KK Loverdos
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 a98e1a6e Christos KK Loverdos
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 a98e1a6e Christos KK Loverdos
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
26 a98e1a6e Christos KK Loverdos
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27 a98e1a6e Christos KK Loverdos
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 a98e1a6e Christos KK Loverdos
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29 a98e1a6e Christos KK Loverdos
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 a98e1a6e Christos KK Loverdos
 * POSSIBILITY OF SUCH DAMAGE.
31 a98e1a6e Christos KK Loverdos
 *
32 a98e1a6e Christos KK Loverdos
 * The views and conclusions contained in the software and
33 a98e1a6e Christos KK Loverdos
 * documentation are those of the authors and should not be
34 a98e1a6e Christos KK Loverdos
 * interpreted as representing official policies, either expressed
35 a98e1a6e Christos KK Loverdos
 * or implied, of GRNET S.A.
36 a98e1a6e Christos KK Loverdos
 */
37 a98e1a6e Christos KK Loverdos
38 12a4f3c9 Christos KK Loverdos
import cc.spray.can.HttpMethods.{GET, POST}
39 16d545ba Christos KK Loverdos
import cc.spray.can._
40 8ad5641a Christos KK Loverdos
import gr.grnet.aquarium.util.Loggable
41 8ad5641a Christos KK Loverdos
import net.liftweb.json.JsonAST.JValue
42 11bbaad8 Christos KK Loverdos
import net.liftweb.json.{JsonAST, Printer}
43 1a63ed88 Christos KK Loverdos
import gr.grnet.aquarium.MasterConf
44 1a63ed88 Christos KK Loverdos
import akka.actor.{ActorRef, Actor}
45 1a63ed88 Christos KK Loverdos
import gr.grnet.aquarium.processor.actor.{RESTResponse, RESTRequest}
46 4dcd61e8 Christos KK Loverdos
import gr.grnet.aquarium.actor.{RESTRole, AquariumActor, DispatcherRole}
47 a98e1a6e Christos KK Loverdos
48 a98e1a6e Christos KK Loverdos
/**
49 12a4f3c9 Christos KK Loverdos
 * Spray-based REST service. This is the outer-world's interface to Aquarium functionality.
50 16d545ba Christos KK Loverdos
 *
51 a98e1a6e Christos KK Loverdos
 * @author Christos KK Loverdos <loverdos@gmail.com>.
52 a98e1a6e Christos KK Loverdos
 */
53 7cfe5091 Christos KK Loverdos
class RESTActor(_id: String) extends AquariumActor with Loggable {
54 7cfe5091 Christos KK Loverdos
  def this() = this("spray-root-service")
55 7cfe5091 Christos KK Loverdos
56 16d545ba Christos KK Loverdos
  self.id = _id
57 a98e1a6e Christos KK Loverdos
58 12a4f3c9 Christos KK Loverdos
  private def jsonResponse200(body: JValue, pretty: Boolean = false): HttpResponse = {
59 12a4f3c9 Christos KK Loverdos
    val stringBody = Printer.pretty(JsonAST.render(body))
60 12a4f3c9 Christos KK Loverdos
    stringResponse200(stringBody, "application/json")
61 12a4f3c9 Christos KK Loverdos
  }
62 12a4f3c9 Christos KK Loverdos
  
63 12a4f3c9 Christos KK Loverdos
  private def stringResponse(status: Int, stringBody: String, contentType: String = "application/json"): HttpResponse = {
64 12a4f3c9 Christos KK Loverdos
    HttpResponse(
65 12a4f3c9 Christos KK Loverdos
      status,
66 12a4f3c9 Christos KK Loverdos
      HttpHeader("Content-type", "%s;charset=utf-8".format(contentType)) :: Nil,
67 12a4f3c9 Christos KK Loverdos
      stringBody.getBytes("UTF-8")
68 12a4f3c9 Christos KK Loverdos
    )
69 12a4f3c9 Christos KK Loverdos
  }
70 12a4f3c9 Christos KK Loverdos
71 12a4f3c9 Christos KK Loverdos
  private def stringResponse200(stringBody: String, contentType: String = "application/json"): HttpResponse = {
72 12a4f3c9 Christos KK Loverdos
    stringResponse(200, stringBody, contentType)
73 12a4f3c9 Christos KK Loverdos
  }
74 16d545ba Christos KK Loverdos
75 12a4f3c9 Christos KK Loverdos
  protected def receive = {
76 1a63ed88 Christos KK Loverdos
    case RequestContext(HttpRequest(GET, "/ping", _, _, _), _, responder) ⇒
77 d305617f Christos KK Loverdos
      responder.complete(stringResponse200("{\"pong\": %s}".format(System.currentTimeMillis())))
78 16d545ba Christos KK Loverdos
79 1a63ed88 Christos KK Loverdos
    case RequestContext(HttpRequest(GET, "/stats", _, _, _), _, responder) ⇒ {
80 16d545ba Christos KK Loverdos
      (serverActor ? GetStats).mapTo[Stats].onComplete {
81 16d545ba Christos KK Loverdos
        future =>
82 16d545ba Christos KK Loverdos
          future.value.get match {
83 16d545ba Christos KK Loverdos
            case Right(stats) => responder.complete {
84 12a4f3c9 Christos KK Loverdos
              stringResponse200 (
85 16d545ba Christos KK Loverdos
                "Uptime              : " + (stats.uptime / 1000.0) + " sec\n" +
86 16d545ba Christos KK Loverdos
                  "Requests dispatched : " + stats.requestsDispatched + '\n' +
87 16d545ba Christos KK Loverdos
                  "Requests timed out  : " + stats.requestsTimedOut + '\n' +
88 16d545ba Christos KK Loverdos
                  "Requests open       : " + stats.requestsOpen + '\n' +
89 16d545ba Christos KK Loverdos
                  "Open connections    : " + stats.connectionsOpen + '\n'
90 12a4f3c9 Christos KK Loverdos
              )
91 16d545ba Christos KK Loverdos
            }
92 12a4f3c9 Christos KK Loverdos
            case Left(ex) => responder.complete(stringResponse(500, "Couldn't get server stats due to " + ex, "text/plain"))
93 16d545ba Christos KK Loverdos
          }
94 16d545ba Christos KK Loverdos
      }
95 16d545ba Christos KK Loverdos
    }
96 16d545ba Christos KK Loverdos
97 1a63ed88 Christos KK Loverdos
    case RequestContext(HttpRequest(post@POST, "/events", headers, body, protocol), _, responder) ⇒
98 12a4f3c9 Christos KK Loverdos
      // POST events here.
99 1a63ed88 Christos KK Loverdos
      val masterConf = MasterConf.MasterConf
100 1a63ed88 Christos KK Loverdos
      val actorProvider = masterConf.actorProvider
101 1a63ed88 Christos KK Loverdos
      val dispatcher = actorProvider.actorForRole(DispatcherRole)
102 1a63ed88 Christos KK Loverdos
      val headersMap = headers map { h => (h.name, h.value) } toMap
103 1a63ed88 Christos KK Loverdos
      val futureResponse = dispatcher ask RESTRequest("POST", "/events", headersMap, body)
104 1a63ed88 Christos KK Loverdos
105 1a63ed88 Christos KK Loverdos
      futureResponse onComplete { fr ⇒
106 1a63ed88 Christos KK Loverdos
        fr.value match {
107 1a63ed88 Christos KK Loverdos
          case None ⇒
108 1a63ed88 Christos KK Loverdos
            // TODO: Will this ever happen??
109 1a63ed88 Christos KK Loverdos
          case Some(Left(throwable)) ⇒
110 1a63ed88 Christos KK Loverdos
            // TODO: Log something here and give back some more detailed info
111 1a63ed88 Christos KK Loverdos
            responder.complete(stringResponse(500, "Internal Server Error", "text/plain"))
112 1a63ed88 Christos KK Loverdos
          case Some(Right(actualResponse)) ⇒
113 1a63ed88 Christos KK Loverdos
            actualResponse match {
114 1a63ed88 Christos KK Loverdos
              case RESTResponse(status, headers, body) ⇒
115 1a63ed88 Christos KK Loverdos
                responder complete {
116 1a63ed88 Christos KK Loverdos
                  HttpResponse(
117 1a63ed88 Christos KK Loverdos
                    status,
118 1a63ed88 Christos KK Loverdos
                    headers map { case (k, v) => HttpHeader(k, v)} toList,
119 1a63ed88 Christos KK Loverdos
                    body
120 1a63ed88 Christos KK Loverdos
                  )
121 1a63ed88 Christos KK Loverdos
                }
122 1a63ed88 Christos KK Loverdos
              case unknownResponse ⇒
123 1a63ed88 Christos KK Loverdos
                // TODO: Log something here and give back some more detailed info
124 1a63ed88 Christos KK Loverdos
                responder.complete(stringResponse(500, "Internal Server Error", "text/plain"))
125 1a63ed88 Christos KK Loverdos
            }
126 1a63ed88 Christos KK Loverdos
        }
127 1a63ed88 Christos KK Loverdos
      }
128 12a4f3c9 Christos KK Loverdos
129 1a63ed88 Christos KK Loverdos
    case RequestContext(HttpRequest(_, _, _, _, _), _, responder) ⇒
130 12a4f3c9 Christos KK Loverdos
      responder.complete(stringResponse(404, "Unknown resource!", "text/plain"))
131 16d545ba Christos KK Loverdos
132 1a63ed88 Christos KK Loverdos
    case Timeout(method, uri, _, _, _, complete) ⇒ complete {
133 16d545ba Christos KK Loverdos
      HttpResponse(status = 500).withBody("The " + method + " request to '" + uri + "' has timed out...")
134 16d545ba Christos KK Loverdos
    }
135 a98e1a6e Christos KK Loverdos
  }
136 16d545ba Christos KK Loverdos
137 16d545ba Christos KK Loverdos
  ////////////// helpers //////////////
138 16d545ba Christos KK Loverdos
139 16d545ba Christos KK Loverdos
  val defaultHeaders = List(HttpHeader("Content-Type", "text/plain"))
140 16d545ba Christos KK Loverdos
141 16d545ba Christos KK Loverdos
  lazy val serverActor = Actor.registry.actorsFor("spray-can-server").head
142 4dcd61e8 Christos KK Loverdos
143 4dcd61e8 Christos KK Loverdos
  def role = RESTRole
144 a98e1a6e Christos KK Loverdos
}