Wire-up the (mongodb-based for now) event store to the configurator.
[aquarium] / logic / src / main / scala / gr / grnet / aquarium / MasterConf.scala
1 /*
2  * Copyright 2011 GRNET S.A. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or
5  * without modification, are permitted provided that the following
6  * conditions are met:
7  *
8  *   1. Redistributions of source code must retain the above
9  *      copyright notice, this list of conditions and the following
10  *      disclaimer.
11  *
12  *   2. Redistributions in binary form must reproduce the above
13  *      copyright notice, this list of conditions and the following
14  *      disclaimer in the documentation and/or other materials
15  *      provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
18  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
21  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
24  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
25  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
27  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28  * POSSIBILITY OF SUCH DAMAGE.
29  *
30  * The views and conclusions contained in the software and
31  * documentation are those of the authors and should not be
32  * interpreted as representing official policies, either expressed
33  * or implied, of GRNET S.A.
34  */
35
36 package gr.grnet.aquarium
37
38 import actor.{DispatcherRole, ActorProvider}
39 import com.ckkloverdos.resource._
40 import com.ckkloverdos.sys.SysProp
41 import com.ckkloverdos.props.Props
42 import com.ckkloverdos.maybe.{Maybe, Failed, Just, NoVal}
43 import com.ckkloverdos.convert.Converters.{DefaultConverters => TheDefaultConverters}
44 import processor.actor.ConfigureDispatcher
45 import rest.RESTService
46 import store.{EventStore, UserStore}
47 import util.Loggable
48
49 /**
50  * The master configurator. Responsible to load all of application configuration and provide the relevant services.
51  *
52  * @author Christos KK Loverdos <loverdos@gmail.com>.
53  */
54 class MasterConf(val props: Props) extends Loggable {
55   import MasterConf.Keys
56
57   private[this] def newInstance[C : Manifest](className: String): C = {
58     val c = defaultClassLoader.loadClass(className).newInstance().asInstanceOf[C]
59     c match {
60       case configurable: MasterConfigurable ⇒
61         configurable configure this
62         c
63       case configurable: Configurable ⇒
64         configurable configure props
65         c
66       case _ ⇒
67         c
68     }
69   }
70
71   private[this] val _actorProvider: ActorProvider = {
72     val instance = newInstance[ActorProvider](props.getEx(Keys.actor_provider_class))
73     logger.info("Loaded ActorProvider: %s".format(instance.getClass))
74     instance
75   }
76   
77   private[this] val _restService: RESTService = {
78     val instance = newInstance[RESTService](props.getEx(Keys.rest_service_class))
79     logger.info("Loaded RESTService: %s".format(instance.getClass))
80     instance
81   }
82
83   private[this] val _userStore: UserStore = {
84     val instance = newInstance[UserStore](props.getEx(Keys.user_store_class))
85     logger.info("Loaded UserStore: %s".format(instance.getClass))
86     instance
87   }
88
89   private[this] val _eventStore: EventStore = {
90     val instance = newInstance[EventStore](props.getEx(Keys.event_store_class))
91     logger.info("Loaded EventStore: %s".format(instance.getClass))
92     instance
93   }
94
95   def get(prop: String): String =
96     props.get(prop) match {
97       case Just(y) => y
98       case _ => ""
99     }
100
101   def defaultClassLoader = Thread.currentThread().getContextClassLoader
102
103   def startServices(): Unit = {
104     _restService.start()
105     _actorProvider.start()
106
107     _actorProvider.actorForRole(DispatcherRole) ! ConfigureDispatcher(this)
108   }
109
110   def stopServices(): Unit = {
111     _restService.stop()
112     _actorProvider.stop()
113     
114 //    akka.actor.Actor.registry.shutdownAll()
115   }
116
117   def stopServicesWithDelay(millis: Long) {
118     Thread sleep millis
119     stopServices()
120   }
121   
122   def actorProvider = _actorProvider
123
124   def userStore = _userStore
125
126   def eventStore = _eventStore
127 }
128
129 object MasterConf {
130   implicit val DefaultConverters = TheDefaultConverters
131
132   val MasterConfName = "aquarium.properties"
133
134   /**
135    * Current directory resource context.
136    * Normally this should be the application installation directory.
137    *
138    * It takes priority over `ClasspathBaseResourceContext`.
139    */
140   val AppBaseResourceContext = new FileStreamResourceContext(".")
141
142   /**
143    * The venerable /etc resource context
144    */
145   val SlashEtcResourceContext = new FileStreamResourceContext("/etc")
146
147   /**
148    * Class loader resource context.
149    * This has the lowest priority.
150    */
151   val ClasspathBaseResourceContext = new ClassLoaderStreamResourceContext(Thread.currentThread().getContextClassLoader)
152
153   /**
154    * Use this property to override the place where aquarium configuration resides.
155    *
156    * The value of this property is a folder that defines the highest-priority resource context.
157    */
158   val ConfBaseFolderSysProp = SysProp("aquarium.conf.base.folder")
159
160   /**
161    * The resource context used in the application.
162    */
163   lazy val MasterResourceContext = {
164     val rc0 = ClasspathBaseResourceContext
165     val rc1 = AppBaseResourceContext
166     val rc2 = SlashEtcResourceContext
167     val basicContext = new CompositeStreamResourceContext(NoVal, rc2, rc1, rc0)
168     
169     ConfBaseFolderSysProp.value match {
170       case Just(value) ⇒
171         // We have a system override for the configuration location
172         new CompositeStreamResourceContext(Just(basicContext), new FileStreamResourceContext(value))
173       case NoVal ⇒
174         basicContext
175       case Failed(e, m) ⇒
176         throw new RuntimeException(m , e)
177     }
178   }
179
180   lazy val MasterConfResource = {
181     val maybeMCResource = MasterResourceContext getResource MasterConfName
182     maybeMCResource match {
183       case Just(masterConfResource) ⇒
184         masterConfResource
185       case NoVal ⇒
186         throw new RuntimeException("Could not find master configuration file: %s".format(MasterConfName))
187       case Failed(e, m) ⇒
188         throw new RuntimeException(m, e)
189     }
190   }
191
192   lazy val MasterConfProps = {
193     val maybeProps = Props apply MasterConfResource
194     maybeProps match {
195       case Just(props) ⇒
196         props
197       case NoVal ⇒
198         throw new RuntimeException("Could not load master configuration file: %s".format(MasterConfName))
199       case Failed(e, m) ⇒
200         throw new RuntimeException(m, e)
201     }
202   }
203
204   lazy val MasterConf = {
205     Maybe(new MasterConf(MasterConfProps)) match {
206       case Just(masterConf) ⇒
207         masterConf
208       case NoVal ⇒
209         throw new RuntimeException("Could not initialize master configuration file: %s".format(MasterConfName))
210       case Failed(e, m) ⇒
211         throw new RuntimeException(m, e)
212     }
213   }
214
215   /**
216    * Defines the names of all the known keys inside the master properties file.
217    */
218   final object Keys {
219     /**
220      * The Aquarium version. Will be reported in any due occasion.
221      */
222     final val version = "version"
223
224     /**
225      * The fully qualified name of the class that implements the `ActorProvider`.
226      * Will be instantiated reflectively and should have a public default constructor.
227      */
228     final val actor_provider_class = "actor.provider.class"
229
230     /**
231      * The class that initializes the REST service
232      */
233     final val rest_service_class = "rest.service.class"
234
235     /**
236      * The class that implements the User store
237      */
238     final val user_store_class = "user.store.class"
239
240     /**
241      * The class that implements the event store
242      */
243     final val event_store_class = "event.store.class"
244
245     /**
246      * Comma separated list of amqp servers running in active-active
247      * configuration.
248      */
249     final val amqp_servers = "amqp.servers"
250
251     /**
252      * Comma separated list of amqp servers running in active-active
253      * configuration.
254      */
255     final val amqp_port = "amqp.port"
256
257     /**
258      * User name for connecting with the AMQP server
259      */
260     final val amqp_username = "amqp.username"
261
262     /**
263      * Passwd for connecting with the AMQP server
264      */
265     final val amqp_password = "amqp.passwd"
266
267     /**
268      * Virtual host on the AMQP server
269      */
270     final val amqp_vhost = "amqp.vhost"
271
272     /**
273      * Comma separated list of exchanges known to aquarium
274      */
275     final val amqp_exchanges = "amqp.exchanges"
276
277     /**
278      * REST service listening port.
279      *
280      * Default is 8080.
281      */
282     final val rest_port = "rest.port"
283
284     /*
285      * Provider for persistence services
286      */
287     final val persistence_provider = "persistence.provider"
288
289     /**
290      * Hostname for the persistence service
291      */
292     final val persistence_host = "persistence.host"
293
294     /**
295      * Username for connecting to the persistence service
296      */
297     final val persistence_username = "persistence.username"
298
299     /**
300      *  Password for connecting to the persistence service
301      */
302     final val persistence_password = "persistence.password"
303
304     /**
305      *  Password for connecting to the persistence service
306      */
307     final val persistence_port = "persistence.port"
308
309     /**
310      *  The DB schema to use
311      */
312     final val persistence_db = "persistence.db"
313   }
314 }