Introduce MasterConf, assuming an aquarium.properties
authorChristos KK Loverdos <loverdos@gmail.com>
Fri, 9 Dec 2011 09:58:11 +0000 (11:58 +0200)
committerChristos KK Loverdos <loverdos@gmail.com>
Fri, 9 Dec 2011 09:58:11 +0000 (11:58 +0200)
Also did some reorganization of actor stuff.

logic/src/main/scala/gr/grnet/aquarium/MasterConf.scala [new file with mode: 0644]
logic/src/main/scala/gr/grnet/aquarium/actor/ActorRole.scala [new file with mode: 0644]
logic/src/main/scala/gr/grnet/aquarium/actor/AquariumActor.scala [moved from logic/src/main/scala/gr/grnet/aquarium/processor/actor/AquariumActor.scala with 93% similarity]
logic/src/main/scala/gr/grnet/aquarium/processor/actor/AquariumDispatcher.scala
logic/src/main/scala/gr/grnet/aquarium/processor/actor/Constants.scala [moved from logic/src/main/scala/gr/grnet/aquarium/processor/actor/AquariumActorRoles.scala with 83% similarity]
logic/src/main/scala/gr/grnet/aquarium/processor/actor/ResourceEventProcessor.scala
logic/src/main/scala/gr/grnet/aquarium/rest/akka/service/AquariumRESTService.scala
logic/src/test/resources/aquarium.properties [new file with mode: 0644]
logic/src/test/scala/gr/grnet/aquarium/MasterConfTest.scala [new file with mode: 0644]
logic/src/test/scala/gr/grnet/aquarium/processor/actor/RemoteActorTest.scala
project/build/Aquarium.scala

diff --git a/logic/src/main/scala/gr/grnet/aquarium/MasterConf.scala b/logic/src/main/scala/gr/grnet/aquarium/MasterConf.scala
new file mode 100644 (file)
index 0000000..d5f00a4
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2011 GRNET S.A. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above
+ *      copyright notice, this list of conditions and the following
+ *      disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above
+ *      copyright notice, this list of conditions and the following
+ *      disclaimer in the documentation and/or other materials
+ *      provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and
+ * documentation are those of the authors and should not be
+ * interpreted as representing official policies, either expressed
+ * or implied, of GRNET S.A.
+ */
+
+package gr.grnet.aquarium
+
+import com.ckkloverdos.resource._
+import com.ckkloverdos.sys.SysProp
+import com.ckkloverdos.maybe.{Failed, Just, NoVal}
+
+/**
+ * The master configurator. Responsible to load all of application configuration and provide the relevant services.
+ *
+ * @author Christos KK Loverdos <loverdos@gmail.com>.
+ */
+class MasterConf(masterConf: StreamResource) {
+}
+
+object MasterConf {
+  val MasterConfName = "aquarium.properties"
+
+  /**
+   * Current directory resource context.
+   * Normally this should be the application installation directory.
+   *
+   * It takes priority over `ClasspathBaseResourceContext`.
+   */
+  val AppBaseResourceContext = new FileStreamResourceContext(".")
+
+  /**
+   * The venerable /etc resource context
+   */
+  val SlashEtcResourceContext = new FileStreamResourceContext("/etc")
+
+  /**
+   * Class loader resource context.
+   * This has the lowest priority.
+   */
+  val ClasspathBaseResourceContext = new ClassLoaderStreamResourceContext(Thread.currentThread().getContextClassLoader)
+
+  /**
+   * Use this property to override the place where aquarium configuration resides.
+   *
+   * The value of this property is a folder that defines the highest-priority resource context.
+   */
+  val ConfBaseFolderSysProp = SysProp("aquarium.conf.base.folder")
+
+  /**
+   * The default resource context used in the application. If you need toi
+   */
+  lazy val MasterResourceContext = {
+    val rc0 = ClasspathBaseResourceContext
+    val rc1 = AppBaseResourceContext
+    val rc2 = SlashEtcResourceContext
+    val basicContext = new CompositeStreamResourceContext(NoVal, rc2, rc1, rc0)
+    
+    ConfBaseFolderSysProp.value match {
+      case Just(value) ⇒
+        // We have a system override for the configuration location
+        new CompositeStreamResourceContext(Just(basicContext), new FileStreamResourceContext(value))
+      case NoVal ⇒
+        basicContext
+      case Failed(e, m) ⇒
+        throw new RuntimeException(m , e)
+    }
+  }
+
+  lazy val MasterConfResource = {
+    val maybeMCResource = MasterResourceContext getResource MasterConfName
+    maybeMCResource match {
+      case Just(masterConfResource) ⇒
+        masterConfResource
+      case NoVal ⇒
+        throw new RuntimeException("Could not find master configuration file: %s".format(MasterConfName))
+      case Failed(e, m) ⇒
+        throw new RuntimeException(m, e)
+    }
+  }
+
+  lazy val MasterConf = new MasterConf(MasterConfResource)
+}
\ No newline at end of file
diff --git a/logic/src/main/scala/gr/grnet/aquarium/actor/ActorRole.scala b/logic/src/main/scala/gr/grnet/aquarium/actor/ActorRole.scala
new file mode 100644 (file)
index 0000000..73a5f4c
--- /dev/null
@@ -0,0 +1,48 @@
+package gr.grnet.aquarium.actor
+
+/*
+ * Copyright 2011 GRNET S.A. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above
+ *      copyright notice, this list of conditions and the following
+ *      disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above
+ *      copyright notice, this list of conditions and the following
+ *      disclaimer in the documentation and/or other materials
+ *      provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and
+ * documentation are those of the authors and should not be
+ * interpreted as representing official policies, either expressed
+ * or implied, of GRNET S.A.
+ */
+
+sealed abstract class ActorRole(val role: String)
+
+/**
+ * The generic router/dispatcher.
+ */
+case object DispatcherRole extends ActorRole("DispatcherRole")
+
+/**
+ * Processes user-related resource events.
+ */
+case object ResourceProcessorRole extends ActorRole("ResourceProcessorRole")
@@ -33,9 +33,9 @@
  * or implied, of GRNET S.A.
  */
 
-package gr.grnet.aquarium.processor.actor
+package gr.grnet.aquarium
+package actor
 
-import AquariumActorRoles.AquariumActorRole
 import akka.actor.Actor
 
 /**
@@ -44,5 +44,5 @@ import akka.actor.Actor
  * @author Christos KK Loverdos <loverdos@gmail.com>.
  */
 trait AquariumActor extends Actor {
-  def role: AquariumActorRole
+  def role: ActorRole
 }
\ No newline at end of file
index 7de0958..f74fbf7 100644 (file)
@@ -37,13 +37,14 @@ package gr.grnet.aquarium.processor.actor
 
 import gr.grnet.aquarium.logic.events.ResourceEvent
 import akka.actor.ActorRef
+import gr.grnet.aquarium.actor.{DispatcherRole, AquariumActor}
 
 /**
  * 
  * @author Christos KK Loverdos <loverdos@gmail.com>.
  */
 class AquariumDispatcher extends AquariumActor {
-  def role = AquariumActorRoles.Dispatcher
+  def role = DispatcherRole
 
   def resourceEventProcessor: ActorRef =
     AquariumActorFactory.get.makeResourceEventProcessor
@@ -39,17 +39,11 @@ package gr.grnet.aquarium.processor.actor
  * 
  * @author Christos KK Loverdos <loverdos@gmail.com>.
  */
-object AquariumActorRoles {
-  sealed abstract class AquariumActorRole(val role: String)
-
-  /**
-   * The generic router/dispatcher.
-   */
-  case object Dispatcher extends AquariumActorRole("Dispatcher")
-
-
-  /**
-   * Processes user-related resource events.
-   */
-  case object ResourceProcessor extends AquariumActorRole("ResourceProcessor")
-}
\ No newline at end of file
+object Constants {
+  val RemoteHost = "localhost"
+  val RemotePort = 2552
+  val LocalHost  = "localhost"
+  val LocalPort  = 2551
+  val ActorNameEcho = "echo"
+  val ActorNameSilent = "silent"
+}
index 1f031f4..b267229 100644 (file)
 package gr.grnet.aquarium.processor.actor
 
 import gr.grnet.aquarium.logic.events.ResourceEvent
+
 import akka.event.EventHandler
+import gr.grnet.aquarium.actor.{ResourceProcessorRole, AquariumActor}
 
 /**
  * 
  * @author Christos KK Loverdos <loverdos@gmail.com>.
  */
 class ResourceEventProcessor extends AquariumActor with ReflectiveAquariumActor {
-  def role = AquariumActorRoles.ResourceProcessor
+  def role = ResourceProcessorRole
 
   def knownMessageTypes = List(classOf[ResourceEvent])
 
index a76a254..fa79336 100644 (file)
@@ -35,8 +35,7 @@
 
 package gr.grnet.aquarium.rest.akka.service
 
-import org.slf4j.LoggerFactory
-import cc.spray.can.HttpMethods.GET
+import cc.spray.can.HttpMethods.{GET, POST}
 import cc.spray.can._
 import akka.actor.Actor
 import gr.grnet.aquarium.util.Loggable
@@ -44,50 +43,59 @@ import net.liftweb.json.JsonAST.JValue
 import net.liftweb.json.{JsonAST, Printer}
 
 /**
+ * Spray-based REST service. This is the outer-world's interface to Aquarium functionality.
  *
  * @author Christos KK Loverdos <loverdos@gmail.com>.
  */
 class AquariumRESTService(_id: String = "spray-root-service", version: String) extends Actor with Loggable {
   self.id = _id
 
-//  private[this] def jsonResponseOK(body: JValue, pretty: Boolean = false): HttpResponse = {
-//    HttpResponse(
-//      200,
-//      HttpHeader("Content-type", "application/json;charset=utf-8") :: Nil,
-//      if(pretty) {
-//        Printer.pretty(JsonAST.render(body)).getB
-//      } else {
-//        bo
-//      }
-//      )
-//  }
-  protected def receive = {
-    case RequestContext(HttpRequest(GET, "/", _, _, _), _, responder) =>
-      responder.complete(index)
+  private def jsonResponse200(body: JValue, pretty: Boolean = false): HttpResponse = {
+    val stringBody = Printer.pretty(JsonAST.render(body))
+    stringResponse200(stringBody, "application/json")
+  }
+  
+  private def stringResponse(status: Int, stringBody: String, contentType: String = "application/json"): HttpResponse = {
+    HttpResponse(
+      status,
+      HttpHeader("Content-type", "%s;charset=utf-8".format(contentType)) :: Nil,
+      stringBody.getBytes("UTF-8")
+    )
+  }
+
+  private def stringResponse200(stringBody: String, contentType: String = "application/json"): HttpResponse = {
+    stringResponse(200, stringBody, contentType)
+  }
 
+  protected def receive = {
     case RequestContext(HttpRequest(GET, "/ping", _, _, _), _, responder) =>
-      responder.complete(response("PONG!"))
+      responder.complete(stringResponse200("{pong: %s}".format(System.currentTimeMillis())))
 
     case RequestContext(HttpRequest(GET, "/stats", _, _, _), _, responder) => {
       (serverActor ? GetStats).mapTo[Stats].onComplete {
         future =>
           future.value.get match {
             case Right(stats) => responder.complete {
-              response {
+              stringResponse200 (
                 "Uptime              : " + (stats.uptime / 1000.0) + " sec\n" +
                   "Requests dispatched : " + stats.requestsDispatched + '\n' +
                   "Requests timed out  : " + stats.requestsTimedOut + '\n' +
                   "Requests open       : " + stats.requestsOpen + '\n' +
                   "Open connections    : " + stats.connectionsOpen + '\n'
-              }
+              )
             }
-            case Left(ex) => responder.complete(response("Couldn't get server stats due to " + ex, status = 500))
+            case Left(ex) => responder.complete(stringResponse(500, "Couldn't get server stats due to " + ex, "text/plain"))
           }
       }
     }
 
+    case RequestContext(HttpRequest(POST, "/events", _, _, _), _, responder) =>
+      // POST events here.
+      // FIXME: implement
+      responder.complete(stringResponse200("{pong: %s}".format(System.currentTimeMillis())))
+
     case RequestContext(HttpRequest(_, _, _, _, _), _, responder) =>
-      responder.complete(response("Unknown resource!", 404))
+      responder.complete(stringResponse(404, "Unknown resource!", "text/plain"))
 
     case Timeout(method, uri, _, _, _, complete) => complete {
       HttpResponse(status = 500).withBody("The " + method + " request to '" + uri + "' has timed out...")
@@ -99,27 +107,4 @@ class AquariumRESTService(_id: String = "spray-root-service", version: String) e
   val defaultHeaders = List(HttpHeader("Content-Type", "text/plain"))
 
   lazy val serverActor = Actor.registry.actorsFor("spray-can-server").head
-
-  def response(msg: String, status: Int = 200) = HttpResponse(status, defaultHeaders, msg.getBytes("ISO-8859-1"))
-
-  lazy val index = HttpResponse(
-    headers = List(HttpHeader("Content-Type", "text/html")),
-    body =
-      <html>
-        <body>
-          <h1>Say hello to
-            <i>spray-can</i>
-            !</h1>
-          <p>Defined resources:</p>
-          <ul>
-            <li>
-              <a href="/ping">/ping</a>
-            </li>
-            <li>
-              <a href="/stats">/stats</a>
-            </li>
-          </ul>
-        </body>
-      </html>.toString.getBytes("UTF-8")
-  )
 }
\ No newline at end of file
diff --git a/logic/src/test/resources/aquarium.properties b/logic/src/test/resources/aquarium.properties
new file mode 100644 (file)
index 0000000..26a7a85
--- /dev/null
@@ -0,0 +1,2 @@
+version=0.0.2-SNAPSHOT
+
diff --git a/logic/src/test/scala/gr/grnet/aquarium/MasterConfTest.scala b/logic/src/test/scala/gr/grnet/aquarium/MasterConfTest.scala
new file mode 100644 (file)
index 0000000..6f62b91
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2011 GRNET S.A. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above
+ *      copyright notice, this list of conditions and the following
+ *      disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above
+ *      copyright notice, this list of conditions and the following
+ *      disclaimer in the documentation and/or other materials
+ *      provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and
+ * documentation are those of the authors and should not be
+ * interpreted as representing official policies, either expressed
+ * or implied, of GRNET S.A.
+ */
+
+package gr.grnet.aquarium
+
+import org.junit.Test
+import org.junit.Assert._
+import org.junit.Assume.assumeTrue
+
+/**
+ * 
+ * @author Christos KK Loverdos <loverdos@gmail.com>.
+ */
+class MasterConfTest {
+  @Test
+  def testExists: Unit = {
+    val rc = MasterConf.MasterConfResource
+    assertTrue(MasterConf.MasterConfName, rc.exists)
+  }
+}
\ No newline at end of file
index 7f701e3..a1cef9b 100644 (file)
@@ -44,14 +44,6 @@ import java.lang.Object
 
 //import akka.actor.Actor.
 
-object Constants {
-  val RemoteHost = "localhost"
-  val RemotePort = 2552
-  val LocalHost  = "localhost"
-  val LocalPort  = 2551
-  val ActorNameEcho = "echo"
-  val ActorNameSilent = "silent"
-}
 
 class EchoActor extends Actor {
   def receive = {
@@ -67,15 +59,6 @@ class SilentActor extends Actor {
   }
 }
 
-abstract class ActorProxy(name: String, host: String = Constants.RemoteHost, port: Int = Constants.RemotePort) extends Actor {
-  val remote = Actor.remote.actorFor(name, host, port)
-
-  def receive = {
-    case message =>
-      remote ! message
-  }
-}
-
 class EchoProxy extends ActorProxy("echo")
 class SilentProxy extends ActorProxy("silent")
 
index 0796b53..355775d 100644 (file)
@@ -65,12 +65,13 @@ class Aquarium(info: ProjectInfo) extends ParentProject(info) {
                val lib_yaml      = "org.yaml"       % "snakeyaml"       % "1.9"  withSources()
                val lib_jcrontab  = "com.kenai.crontab-parser" % "crontab-parser" % "1.0.1" withSources()
                val lib_xstream   = "com.thoughtworks.xstream" % "xstream"     % "1.4.1" withSources()
-               val lib_rabbit    = "com.rabbitmq"   % "amqp-client"       % "2.7.0" withSources()
+               val lib_rabbit    = "com.rabbitmq"   % "amqp-client"       % "2.5.0" withSources()
                val lib_mongo     = "org.mongodb"    % "mongo-java-driver" % "2.7.2" withSources()
        //val lib_casbah    = "com.mongodb.casbah" % "casbah-core_2.9.1"  % "2.1.5-1" withSources()
-       val lib_akka_actor  = "se.scalablesolutions.akka" % "akka-actor" % "1.3-RC1" withSources()
-       val lib_akka_remote = "se.scalablesolutions.akka" % "akka-remote" % "1.3-RC1" withSources()
-       val lib_akka_test   = "se.scalablesolutions.akka" % "akka-testkit" % "1.3-RC1" % "test" withSources()
+       val lib_akka_actor  = "se.scalablesolutions.akka" % "akka-actor"   % "1.3-RC2" withSources()
+       val lib_akka_remote = "se.scalablesolutions.akka" % "akka-remote"  % "1.3-RC2" withSources()
+       val lib_akka_test   = "se.scalablesolutions.akka" % "akka-testkit" % "1.3-RC2" % "test" withSources()
+       val lib_akka_amqp   = "se.scalablesolutions.akka" % "akka-amqp"    % "1.3-RC2" withSources()
 
        val lib_javaxrs     = "javax.ws.rs" % "jsr311-api" % "1.1.1" withSources()
        val lib_spray_can   = "cc.spray.can" % "spray-can" % "0.9.2-SNAPSHOT" withSources()