2 * Copyright 2011 GRNET S.A. All rights reserved.
4 * Redistribution and use in source and binary forms, with or
5 * without modification, are permitted provided that the following
8 * 1. Redistributions of source code must retain the above
9 * copyright notice, this list of conditions and the following
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.
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.
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.
36 package gr.grnet.aquarium
38 import actor.{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.{ResourceEventProcessorService}
46 import util.{Lifecycle, Loggable}
49 * The master configurator. Responsible to load all of application configuration and provide the relevant services.
51 * @author Christos KK Loverdos <loverdos@gmail.com>.
53 class MasterConf(val props: Props) extends Loggable {
54 import MasterConf.Keys
57 * Reflectively provide a new instance of a class and configure it appropriately.
59 private[this] def newInstance[C : Manifest](className: String): C = {
60 val instanceM = Maybe(defaultClassLoader.loadClass(className).newInstance().asInstanceOf[C])
62 case Just(instance) ⇒ instance match {
63 case configurable: Configurable ⇒
64 Maybe(configurable configure props) match {
68 throw new Exception("Could not configure instance of %s".format(className), e)
70 throw new Exception("Could not configure instance of %s".format(className))
76 throw new Exception("Could not instantiate %s".format(className), e)
78 throw new Exception("Could not instantiate %s".format(className))
83 private[this] lazy val _actorProvider: ActorProvider = {
84 val instance = newInstance[ActorProvider](props.getEx(Keys.actor_provider_class))
85 logger.info("Loaded ActorProvider: %s".format(instance.getClass))
89 private[this] lazy val _storeProvider: StoreProvider = {
90 val instance = newInstance[StoreProvider](props.getEx(Keys.store_provider_class))
91 logger.info("Loaded StoreProvider: %s".format(instance.getClass))
95 private[this] lazy val _restService: Lifecycle = {
96 val instance = newInstance[Lifecycle](props.getEx(Keys.rest_service_class))
97 logger.info("Loaded RESTService: %s".format(instance.getClass))
101 private[this] lazy val _userStoreM: Maybe[UserStore] = {
102 // If there is a specific `UserStore` implementation specified in the
103 // properties, then this implementation overrides the user store given by
105 props.get(Keys.userstore_class) map { className ⇒
106 val instance = newInstance[UserStore](className)
107 logger.info("Overriding UserStore provisioning. Implementation given by: %s".format(instance.getClass))
112 private[this] lazy val _eventStoreM: Maybe[EventStore] = {
113 // If there is a specific `EventStore` implementation specified in the
114 // properties, then this implementation overrides the event store given by
116 props.get(Keys.eventstore_class) map { className ⇒
117 val instance = newInstance[EventStore](className)
118 logger.info("Overriding EventStore provisioning. Implementation given by: %s".format(instance.getClass))
123 private[this] lazy val _IMEventStoreM: Maybe[IMStore] = {
124 // If there is a specific `IMStore` implementation specified in the
125 // properties, then this implementation overrides the event store given by
127 props.get(Keys.im_eventstore_class) map { className ⇒
128 val instance = newInstance[IMStore](className)
129 logger.info("Overriding IMStore provisioning. Implementation given by: %s".format(instance.getClass))
134 private[this] lazy val _WalletEventStoreM: Maybe[WalletStore] = {
135 // If there is a specific `IMStore` implementation specified in the
136 // properties, then this implementation overrides the event store given by
138 props.get(Keys.walletstore_class) map { className ⇒
139 val instance = newInstance[WalletStore](className)
140 logger.info("Overriding WalletStore provisioning. Implementation given by: %s".format(instance.getClass))
145 private[this] lazy val _resEventProc: ResourceEventProcessorService = {
146 new ResourceEventProcessorService()
149 def get(prop: String): String =
150 props.get(prop) match {
155 def defaultClassLoader = Thread.currentThread().getContextClassLoader
157 def startServices(): Unit = {
159 _actorProvider.start()
160 _resEventProc.start()
163 def stopServices(): Unit = {
166 _actorProvider.stop()
169 // akka.actor.Actor.registry.shutdownAll()
172 def stopServicesWithDelay(millis: Long) {
177 def actorProvider = _actorProvider
182 case _ ⇒ storeProvider.userStore
189 case _ ⇒ storeProvider.eventStore
194 _IMEventStoreM match {
196 case _ ⇒ storeProvider.imStore
201 _WalletEventStoreM match {
203 case _ ⇒ storeProvider.walletStore
207 def storeProvider = _storeProvider
211 implicit val DefaultConverters = TheDefaultConverters
213 val MasterConfName = "aquarium.properties"
216 * Current directory resource context.
217 * Normally this should be the application installation directory.
219 * It takes priority over `ClasspathBaseResourceContext`.
221 val AppBaseResourceContext = new FileStreamResourceContext(".")
224 * The venerable /etc resource context
226 val SlashEtcResourceContext = new FileStreamResourceContext("/etc")
229 * Class loader resource context.
230 * This has the lowest priority.
232 val ClasspathBaseResourceContext = new ClassLoaderStreamResourceContext(Thread.currentThread().getContextClassLoader)
235 * Use this property to override the place where aquarium configuration resides.
237 * The value of this property is a folder that defines the highest-priority resource context.
239 val ConfBaseFolderSysProp = SysProp("aquarium.conf.base.folder")
242 * The resource context used in the application.
244 lazy val MasterResourceContext = {
245 val rc0 = ClasspathBaseResourceContext
246 val rc1 = AppBaseResourceContext
247 val rc2 = SlashEtcResourceContext
248 val basicContext = new CompositeStreamResourceContext(NoVal, rc2, rc1, rc0)
250 ConfBaseFolderSysProp.value match {
252 // We have a system override for the configuration location
253 new CompositeStreamResourceContext(Just(basicContext), new FileStreamResourceContext(value))
257 throw new RuntimeException(m , e)
261 lazy val MasterConfResource = {
262 val maybeMCResource = MasterResourceContext getResource MasterConfName
263 maybeMCResource match {
264 case Just(masterConfResource) ⇒
267 throw new RuntimeException("Could not find master configuration file: %s".format(MasterConfName))
269 throw new RuntimeException(m, e)
273 lazy val MasterConfProps = {
274 val maybeProps = Props apply MasterConfResource
279 throw new RuntimeException("Could not load master configuration file: %s".format(MasterConfName))
281 throw new RuntimeException(m, e)
285 lazy val MasterConf = {
286 Maybe(new MasterConf(MasterConfProps)) match {
287 case Just(masterConf) ⇒
290 throw new RuntimeException("Could not initialize master configuration file: %s".format(MasterConfName))
292 throw new RuntimeException(m, e)
297 * Defines the names of all the known keys inside the master properties file.
301 * The Aquarium version. Will be reported in any due occasion.
303 final val version = "version"
306 * The fully qualified name of the class that implements the `ActorProvider`.
307 * Will be instantiated reflectively and should have a public default constructor.
309 final val actor_provider_class = "actor.provider.class"
312 * The class that initializes the REST service
314 final val rest_service_class = "rest.service.class"
317 * The fully qualified name of the class that implements the `StoreProvider`.
318 * Will be instantiated reflectively and should have a public default constructor.
320 final val store_provider_class = "store.provider.class"
323 * The class that implements the User store
325 final val userstore_class = "user.store.class"
328 * The class that implements the event store
330 final val eventstore_class = "event.store.class"
333 * The class that implements the IM event store
335 final val im_eventstore_class = "imevent.store.class"
338 * The class that implements the wallet store
340 final val walletstore_class = "wallet.store.class"
342 /** The lower mark for the UserActors' LRU, managed by UserActorManager.
344 * The terminology is borrowed from the (also borrowed) Apache-lucene-solr-based implementation.
347 final val user_actors_lru_lower_mark = "user.actors.LRU.lower.mark"
350 * The upper mark for the UserActors' LRU, managed by UserActorManager.
352 * The terminology is borrowed from the (also borrowed) Apache-lucene-solr-based implementation.
354 final val user_actors_lru_upper_mark = "user.actors.LRU.upper.mark"
357 * Comma separated list of amqp servers running in active-active
360 final val amqp_servers = "amqp.servers"
363 * Comma separated list of amqp servers running in active-active
366 final val amqp_port = "amqp.port"
369 * User name for connecting with the AMQP server
371 final val amqp_username = "amqp.username"
374 * Passwd for connecting with the AMQP server
376 final val amqp_password = "amqp.passwd"
379 * Virtual host on the AMQP server
381 final val amqp_vhost = "amqp.vhost"
384 * Comma separated list of exchanges known to aquarium
386 final val amqp_exchanges = "amqp.exchanges"
389 * REST service listening port.
393 final val rest_port = "rest.port"
396 * Provider for persistence services
398 final val persistence_provider = "persistence.provider"
401 * Hostname for the persistence service
403 final val persistence_host = "persistence.host"
406 * Username for connecting to the persistence service
408 final val persistence_username = "persistence.username"
411 * Password for connecting to the persistence service
413 final val persistence_password = "persistence.password"
416 * Password for connecting to the persistence service
418 final val persistence_port = "persistence.port"
421 * The DB schema to use
423 final val persistence_db = "persistence.db"