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