WIP: Remodeling events
[aquarium] / src / main / scala / gr / grnet / aquarium / Configurator.scala
index 754566a..8811a9f 100644 (file)
 
 package gr.grnet.aquarium
 
-import actor.{ActorProvider}
-import com.ckkloverdos.resource._
-import com.ckkloverdos.sys.SysProp
+import events.im.IMEventModel
+import java.io.File
+
+import com.ckkloverdos.maybe._
 import com.ckkloverdos.props.Props
-import com.ckkloverdos.maybe.{Maybe, Failed, Just, NoVal}
 import com.ckkloverdos.convert.Converters.{DefaultConverters => TheDefaultConverters}
-import processor.actor.{UserEventProcessorService, ResourceEventProcessorService}
-import store._
-import util.{Lifecycle, Loggable}
-import java.io.File
+
+import gr.grnet.aquarium.service._
+import gr.grnet.aquarium.util.{Lifecycle, Loggable}
+import gr.grnet.aquarium.store._
 
 /**
  * The master configurator. Responsible to load all of application configuration and provide the relevant services.
@@ -65,25 +65,25 @@ class Configurator(val props: Props) extends Loggable {
           Maybe(configurable configure props) match {
             case Just(_) ⇒
               instance
-            case Failed(e, _) ⇒
-              throw new Exception("Could not configure instance of %s".format(className), e)
+            case Failed(e) ⇒
+              throw new AquariumException("Could not configure instance of %s".format(className), e)
             case NoVal ⇒
-              throw new Exception("Could not configure instance of %s".format(className))
+              throw new AquariumException("Could not configure instance of %s".format(className))
           }
         case _ ⇒
           instance
       }
-      case Failed(e, _) ⇒
-        throw new Exception("Could not instantiate %s".format(className), e)
+      case Failed(e) ⇒
+        throw new AquariumException("Could not instantiate %s".format(className), e)
       case NoVal ⇒
-        throw new Exception("Could not instantiate %s".format(className))
+        throw new AquariumException("Could not instantiate %s".format(className))
     }
 
   }
 
-  private[this] lazy val _actorProvider: ActorProvider = {
-    val instance = newInstance[ActorProvider](props.getEx(Keys.actor_provider_class))
-    logger.info("Loaded ActorProvider: %s".format(instance.getClass))
+  private[this] lazy val _actorProvider: ActorProviderService = {
+    val instance = newInstance[ActorProviderService](props.getEx(Keys.actor_provider_class))
+    logger.info("Loaded ActorProviderService: %s".format(instance.getClass))
     instance
   }
 
@@ -125,10 +125,10 @@ class Configurator(val props: Props) extends Loggable {
     }
   }
 
-  private[this] lazy val _userEventStoreM: Maybe[UserEventStore] = {
+  private[this] lazy val _imEventStoreM: Maybe[IMEventStore] = {
     props.get(Keys.user_event_store_class) map { className ⇒
-      val instance = newInstance[UserEventStore](className)
-      logger.info("Overriding UserEventStore provisioning. Implementation given by: %s".format(instance.getClass))
+      val instance = newInstance[IMEventStore](className)
+      logger.info("Overriding IMEventStore provisioning. Implementation given by: %s".format(instance.getClass))
       instance
     }
   }
@@ -154,15 +154,57 @@ class Configurator(val props: Props) extends Loggable {
     }
   }
 
-  private[this] lazy val _resEventProc: ResourceEventProcessorService = new ResourceEventProcessorService
+  private[this] lazy val _eventsStoreFolder: Maybe[File] = {
+    props.get(Keys.events_store_folder) map {
+      folderName ⇒
+        val canonicalFolder = {
+          val folder = new File(folderName)
+          if(folder.isAbsolute) {
+            folder.getCanonicalFile
+          } else {
+            logger.info("{} is not absolute, making it relative to AQUARIUM_HOME", Keys.events_store_folder)
+            new File(ResourceLocator.AQUARIUM_HOME_FOLDER, folderName).getCanonicalFile
+          }
+        }
+
+        val canonicalPath = canonicalFolder.getCanonicalPath
+
+        logger.info("{} = {}", Keys.events_store_folder, canonicalPath)
+
+        if(canonicalFolder.exists() && !canonicalFolder.isDirectory) {
+          throw new AquariumException("%s = %s is not a folder".format(Keys.events_store_folder, canonicalFolder))
+        }
+
+        // Now, events folder must be outside AQUARIUM_HOME, since AQUARIUM_HOME can be wiped out for an upgrade but
+        // we still want to keep the events.
+        val ahCanonicalPath = ResourceLocator.AQUARIUM_HOME_FOLDER.getCanonicalPath
+        if(canonicalPath.startsWith(ahCanonicalPath)) {
+          throw new AquariumException(
+            "%s = %s is under %s = %s".format(
+              Keys.events_store_folder, canonicalFolder,
+              ResourceLocator.AQUARIUM_HOME.name, ahCanonicalPath
+            ))
+        }
+
+        canonicalFolder.mkdirs()
+
+        canonicalFolder
+    }
+  }
+
+  private[this] lazy val _resEventProc = new ResourceEventProcessorService
 
-  private[this] lazy val _imEventProc: UserEventProcessorService = new UserEventProcessorService
+  private[this] lazy val _imEventProc = new IMEventProcessorService
+
+  private[this] lazy val _lifecycleServices = List(AkkaService, _restService, _actorProvider, _resEventProc, _imEventProc)
 
   def get(key: String, default: String = ""): String = props.getOr(key, default)
 
   def defaultClassLoader = Thread.currentThread().getContextClassLoader
 
   /**
+   * FIXME: This must be ditched.
+   * 
    * Find a file whose location can be overiden in
    * the configuration file (e.g. policy.yaml)
    *
@@ -187,18 +229,11 @@ class Configurator(val props: Props) extends Loggable {
   }
 
   def startServices(): Unit = {
-    _restService.start()
-    _actorProvider.start()
-    _resEventProc.start()
-    _imEventProc.start()
+    _lifecycleServices.foreach(_.start())
   }
 
   def stopServices(): Unit = {
-    _imEventProc.stop()
-    _resEventProc.stop()
-    _restService.stop()
-    _actorProvider.stop()
-
+    _lifecycleServices.reverse.foreach(_.stop())
 //    akka.actor.Actor.registry.shutdownAll()
   }
 
@@ -230,10 +265,10 @@ class Configurator(val props: Props) extends Loggable {
     }
   }
 
-  def userEventStore = {
-    _userEventStoreM match {
+  def imEventStore = {
+    _imEventStoreM match {
       case Just(es) ⇒ es
-      case _        ⇒ storeProvider.userEventStore
+      case _        ⇒ storeProvider.imEventStore
     }
   }
 
@@ -253,10 +288,11 @@ class Configurator(val props: Props) extends Loggable {
     new Configurator(newProps)
   }
 
-  // FIXME: This is instead of props.getInt which currently contains a bug.
-  // FIXME: Fix the original bug and delete this method
-  def getInt(name: String): Maybe[Int] = {
-    props.get(name).map(_.toInt)
+  def eventsStoreFolder = _eventsStoreFolder
+
+  def adminCookie: MaybeOption[String] = props.get(Configurator.Keys.admin_cookie) match {
+    case just @ Just(_) ⇒ just
+    case _ ⇒ NoVal
   }
 }
 
@@ -275,9 +311,9 @@ object Configurator {
       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)
+        throw new AquariumException("Could not find master configuration file: %s".format(MasterConfName))
+      case Failed(e) ⇒
+        throw new AquariumException(e, "Could not find master configuration file: %s".format(MasterConfName))
     }
   }
 
@@ -287,9 +323,9 @@ object Configurator {
       case Just(props) ⇒
         props
       case NoVal ⇒
-        throw new RuntimeException("Could not load master configuration file: %s".format(MasterConfName))
-      case Failed(e, m) ⇒
-        throw new RuntimeException(m, e)
+        throw new AquariumException("Could not load master configuration file: %s".format(MasterConfName))
+      case Failed(e) ⇒
+        throw new AquariumException(e, "Could not load master configuration file: %s".format(MasterConfName))
     }
   }
 
@@ -298,9 +334,9 @@ object Configurator {
       case Just(masterConf) ⇒
         masterConf
       case NoVal ⇒
-        throw new RuntimeException("Could not initialize master configuration file: %s".format(MasterConfName))
-      case Failed(e, m) ⇒
-        throw new RuntimeException(m, e)
+        throw new AquariumException("Could not initialize master configuration file: %s".format(MasterConfName))
+      case Failed(e) ⇒
+        throw new AquariumException(e, "Could not initialize master configuration file: %s".format(MasterConfName))
     }
   }
 
@@ -315,7 +351,7 @@ object Configurator {
     final val version = "version"
 
     /**
-     * The fully qualified name of the class that implements the `ActorProvider`.
+     * The fully qualified name of the class that implements the `ActorProviderService`.
      * Will be instantiated reflectively and should have a public default constructor.
      */
     final val actor_provider_class = "actor.provider.class"
@@ -493,6 +529,27 @@ object Configurator {
      */
     final val time_unit_in_millis = "time.unit.in.seconds"
 
-    final val message_queues_store_folder = "messages"
+    /**
+     * If a value is given to this property, then it represents a folder where all events coming to aquarium are
+     * stored.
+     */
+    final val events_store_folder = "events.store.folder"
+
+    /**
+     * If set to `true`, then an IM event that cannot be parsed to [[gr.grnet.aquarium.events.im.IMEventModel]] is
+     * saved to the [[gr.grnet.aquarium.store.IMEventStore]].
+     */
+    final val save_unparsed_event_im = "save.unparsed.event.im"
+
+    /**
+     * A cookie used in every administrative REST API call, so that Aquarium knows it comes from
+     * an authorised client.
+     */
+    final val admin_cookie = "admin.cookie"
+  }
+
+  object HTTP {
+    final val RESTAdminHeaderName = "X-Aquarium-Admin-Cookie"
+    final val RESTAdminHeaderNameLowerCase = RESTAdminHeaderName.toLowerCase
   }
 }