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