root / src / main / scala / gr / grnet / aquarium / Configurator.scala @ f1257c64
History | View | Annotate | Download (15.2 kB)
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.{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.{UserEventProcessorService, ResourceEventProcessorService, EventProcessorService} |
45 |
import store._ |
46 |
import util.{Lifecycle, Loggable} |
47 |
|
48 |
/** |
49 |
* The master configurator. Responsible to load all of application configuration and provide the relevant services. |
50 |
* |
51 |
* @author Christos KK Loverdos <loverdos@gmail.com>. |
52 |
*/ |
53 |
class Configurator(val props: Props) extends Loggable { |
54 |
import Configurator.Keys |
55 |
|
56 |
/** |
57 |
* Reflectively provide a new instance of a class and configure it appropriately. |
58 |
*/ |
59 |
private[this] def newInstance[C : Manifest](className: String): C = { |
60 |
val instanceM = Maybe(defaultClassLoader.loadClass(className).newInstance().asInstanceOf[C]) |
61 |
instanceM match { |
62 |
case Just(instance) ⇒ instance match { |
63 |
case configurable: Configurable ⇒ |
64 |
Maybe(configurable configure props) match { |
65 |
case Just(_) ⇒ |
66 |
instance |
67 |
case Failed(e, _) ⇒ |
68 |
throw new Exception("Could not configure instance of %s".format(className), e) |
69 |
case NoVal ⇒ |
70 |
throw new Exception("Could not configure instance of %s".format(className)) |
71 |
} |
72 |
case _ ⇒ |
73 |
instance |
74 |
} |
75 |
case Failed(e, _) ⇒ |
76 |
throw new Exception("Could not instantiate %s".format(className), e) |
77 |
case NoVal ⇒ |
78 |
throw new Exception("Could not instantiate %s".format(className)) |
79 |
} |
80 |
|
81 |
} |
82 |
|
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)) |
86 |
instance |
87 |
} |
88 |
|
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)) |
92 |
instance |
93 |
} |
94 |
|
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)) |
98 |
instance |
99 |
} |
100 |
|
101 |
private[this] lazy val _userStateStoreM: Maybe[UserStateStore] = { |
102 |
// If there is a specific `UserStateStore` implementation specified in the |
103 |
// properties, then this implementation overrides the user store given by |
104 |
// `StoreProvider`. |
105 |
props.get(Keys.user_state_store_class) map { className ⇒ |
106 |
val instance = newInstance[UserStateStore](className) |
107 |
logger.info("Overriding UserStateStore provisioning. Implementation given by: %s".format(instance.getClass)) |
108 |
instance |
109 |
} |
110 |
} |
111 |
|
112 |
private[this] lazy val _resourceEventStoreM: Maybe[ResourceEventStore] = { |
113 |
// If there is a specific `EventStore` implementation specified in the |
114 |
// properties, then this implementation overrides the event store given by |
115 |
// `StoreProvider`. |
116 |
props.get(Keys.resource_event_store_class) map { className ⇒ |
117 |
val instance = newInstance[ResourceEventStore](className) |
118 |
logger.info("Overriding EventStore provisioning. Implementation given by: %s".format(instance.getClass)) |
119 |
instance |
120 |
} |
121 |
} |
122 |
|
123 |
private[this] lazy val _userEventStoreM: Maybe[UserEventStore] = { |
124 |
props.get(Keys.user_event_store_class) map { className ⇒ |
125 |
val instance = newInstance[UserEventStore](className) |
126 |
logger.info("Overriding UserEventStore provisioning. Implementation given by: %s".format(instance.getClass)) |
127 |
instance |
128 |
} |
129 |
} |
130 |
|
131 |
private[this] lazy val _WalletEventStoreM: Maybe[WalletEntryStore] = { |
132 |
// If there is a specific `IMStore` implementation specified in the |
133 |
// properties, then this implementation overrides the event store given by |
134 |
// `IMProvider`. |
135 |
props.get(Keys.wallet_entry_store_class) map { |
136 |
className ⇒ |
137 |
val instance = newInstance[WalletEntryStore](className) |
138 |
logger.info("Overriding WalletEntryStore provisioning. Implementation given by: %s".format(instance.getClass)) |
139 |
instance |
140 |
} |
141 |
} |
142 |
|
143 |
private[this] lazy val _policyStoreM: Maybe[PolicyStore] = { |
144 |
props.get(Keys.policy_store_class) map { |
145 |
className ⇒ |
146 |
val instance = newInstance[PolicyStore](className) |
147 |
logger.info("Overriding PolicyStore provisioning. Implementation given by: %s".format(instance.getClass)) |
148 |
instance |
149 |
} |
150 |
} |
151 |
|
152 |
private[this] lazy val _resEventProc: ResourceEventProcessorService = new ResourceEventProcessorService |
153 |
|
154 |
private[this] lazy val _imEventProc: UserEventProcessorService = new UserEventProcessorService |
155 |
|
156 |
def get(key: String, default: String = ""): String = props.getOr(key, default) |
157 |
|
158 |
def defaultClassLoader = Thread.currentThread().getContextClassLoader |
159 |
|
160 |
def startServices(): Unit = { |
161 |
_restService.start() |
162 |
_actorProvider.start() |
163 |
_resEventProc.start() |
164 |
_imEventProc.start() |
165 |
} |
166 |
|
167 |
def stopServices(): Unit = { |
168 |
_imEventProc.stop() |
169 |
_resEventProc.stop() |
170 |
_restService.stop() |
171 |
_actorProvider.stop() |
172 |
|
173 |
// akka.actor.Actor.registry.shutdownAll() |
174 |
} |
175 |
|
176 |
def stopServicesWithDelay(millis: Long) { |
177 |
Thread sleep millis |
178 |
stopServices() |
179 |
} |
180 |
|
181 |
def actorProvider = _actorProvider |
182 |
|
183 |
def userStateStore = { |
184 |
_userStateStoreM match { |
185 |
case Just(us) ⇒ us |
186 |
case _ ⇒ storeProvider.userStateStore |
187 |
} |
188 |
} |
189 |
|
190 |
def resourceEventStore = { |
191 |
_resourceEventStoreM match { |
192 |
case Just(es) ⇒ es |
193 |
case _ ⇒ storeProvider.resourceEventStore |
194 |
} |
195 |
} |
196 |
|
197 |
def walletStore = { |
198 |
_WalletEventStoreM match { |
199 |
case Just(es) ⇒ es |
200 |
case _ ⇒ storeProvider.walletEntryStore |
201 |
} |
202 |
} |
203 |
|
204 |
def userEventStore = { |
205 |
_userEventStoreM match { |
206 |
case Just(es) ⇒ es |
207 |
case _ ⇒ storeProvider.userEventStore |
208 |
} |
209 |
} |
210 |
|
211 |
def policyEventStore = { |
212 |
_policyStoreM match { |
213 |
case Just(es) ⇒ es |
214 |
case _ ⇒ storeProvider.policyStore |
215 |
} |
216 |
} |
217 |
|
218 |
def storeProvider = _storeProvider |
219 |
} |
220 |
|
221 |
object Configurator { |
222 |
implicit val DefaultConverters = TheDefaultConverters |
223 |
|
224 |
val MasterConfName = "aquarium.properties" |
225 |
|
226 |
/** |
227 |
* Current directory resource context. |
228 |
* Normally this should be the application installation directory. |
229 |
* |
230 |
* It takes priority over `ClasspathBaseResourceContext`. |
231 |
*/ |
232 |
val AppBaseResourceContext = new FileStreamResourceContext(".") |
233 |
|
234 |
/** |
235 |
* The venerable /etc resource context |
236 |
*/ |
237 |
val SlashEtcResourceContext = new FileStreamResourceContext("/etc") |
238 |
|
239 |
/** |
240 |
* Class loader resource context. |
241 |
* This has the lowest priority. |
242 |
*/ |
243 |
val ClasspathBaseResourceContext = new ClassLoaderStreamResourceContext(Thread.currentThread().getContextClassLoader) |
244 |
|
245 |
/** |
246 |
* Use this property to override the place where aquarium configuration resides. |
247 |
* |
248 |
* The value of this property is a folder that defines the highest-priority resource context. |
249 |
*/ |
250 |
val ConfBaseFolderSysProp = SysProp("aquarium.conf.base.folder") |
251 |
|
252 |
/** |
253 |
* The resource context used in the application. |
254 |
*/ |
255 |
lazy val MasterResourceContext = { |
256 |
val rc0 = ClasspathBaseResourceContext |
257 |
val rc1 = AppBaseResourceContext |
258 |
val rc2 = SlashEtcResourceContext |
259 |
val basicContext = new CompositeStreamResourceContext(NoVal, rc2, rc1, rc0) |
260 |
|
261 |
ConfBaseFolderSysProp.value match { |
262 |
case Just(value) ⇒ |
263 |
// We have a system override for the configuration location |
264 |
new CompositeStreamResourceContext(Just(basicContext), new FileStreamResourceContext(value)) |
265 |
case NoVal ⇒ |
266 |
basicContext |
267 |
case Failed(e, m) ⇒ |
268 |
throw new RuntimeException(m , e) |
269 |
} |
270 |
} |
271 |
|
272 |
lazy val MasterConfResource = { |
273 |
val maybeMCResource = MasterResourceContext getResource MasterConfName |
274 |
maybeMCResource match { |
275 |
case Just(masterConfResource) ⇒ |
276 |
masterConfResource |
277 |
case NoVal ⇒ |
278 |
throw new RuntimeException("Could not find master configuration file: %s".format(MasterConfName)) |
279 |
case Failed(e, m) ⇒ |
280 |
throw new RuntimeException(m, e) |
281 |
} |
282 |
} |
283 |
|
284 |
lazy val MasterConfProps = { |
285 |
val maybeProps = Props apply MasterConfResource |
286 |
maybeProps match { |
287 |
case Just(props) ⇒ |
288 |
props |
289 |
case NoVal ⇒ |
290 |
throw new RuntimeException("Could not load master configuration file: %s".format(MasterConfName)) |
291 |
case Failed(e, m) ⇒ |
292 |
throw new RuntimeException(m, e) |
293 |
} |
294 |
} |
295 |
|
296 |
lazy val MasterConfigurator = { |
297 |
Maybe(new Configurator(MasterConfProps)) match { |
298 |
case Just(masterConf) ⇒ |
299 |
masterConf |
300 |
case NoVal ⇒ |
301 |
throw new RuntimeException("Could not initialize master configuration file: %s".format(MasterConfName)) |
302 |
case Failed(e, m) ⇒ |
303 |
throw new RuntimeException(m, e) |
304 |
} |
305 |
} |
306 |
|
307 |
/** |
308 |
* Defines the names of all the known keys inside the master properties file. |
309 |
*/ |
310 |
final object Keys { |
311 |
/** |
312 |
* The Aquarium version. Will be reported in any due occasion. |
313 |
*/ |
314 |
final val version = "version" |
315 |
|
316 |
/** |
317 |
* The fully qualified name of the class that implements the `ActorProvider`. |
318 |
* Will be instantiated reflectively and should have a public default constructor. |
319 |
*/ |
320 |
final val actor_provider_class = "actor.provider.class" |
321 |
|
322 |
/** |
323 |
* The class that initializes the REST service |
324 |
*/ |
325 |
final val rest_service_class = "rest.service.class" |
326 |
|
327 |
/** |
328 |
* The fully qualified name of the class that implements the `StoreProvider`. |
329 |
* Will be instantiated reflectively and should have a public default constructor. |
330 |
*/ |
331 |
final val store_provider_class = "store.provider.class" |
332 |
|
333 |
/** |
334 |
* The class that implements the User store |
335 |
*/ |
336 |
final val user_state_store_class = "user.state.store.class" |
337 |
|
338 |
/** |
339 |
* The class that implements the resource event store |
340 |
*/ |
341 |
final val resource_event_store_class = "resource.event.store.class" |
342 |
|
343 |
/** |
344 |
* The class that implements the IM event store |
345 |
*/ |
346 |
final val user_event_store_class = "user.event.store.class" |
347 |
|
348 |
/** |
349 |
* The class that implements the wallet entries store |
350 |
*/ |
351 |
final val wallet_entry_store_class = "wallet.entry.store.class" |
352 |
|
353 |
/** |
354 |
* The class that implements the wallet entries store |
355 |
*/ |
356 |
final val policy_store_class = "policy.store.class" |
357 |
|
358 |
|
359 |
/** The lower mark for the UserActors' LRU, managed by UserActorManager. |
360 |
* |
361 |
* The terminology is borrowed from the (also borrowed) Apache-lucene-solr-based implementation. |
362 |
* |
363 |
*/ |
364 |
final val user_actors_lru_lower_mark = "user.actors.LRU.lower.mark" |
365 |
|
366 |
/** |
367 |
* The upper mark for the UserActors' LRU, managed by UserActorManager. |
368 |
* |
369 |
* The terminology is borrowed from the (also borrowed) Apache-lucene-solr-based implementation. |
370 |
*/ |
371 |
final val user_actors_lru_upper_mark = "user.actors.LRU.upper.mark" |
372 |
|
373 |
/** |
374 |
* Comma separated list of amqp servers running in active-active |
375 |
* configuration. |
376 |
*/ |
377 |
final val amqp_servers = "amqp.servers" |
378 |
|
379 |
/** |
380 |
* Comma separated list of amqp servers running in active-active |
381 |
* configuration. |
382 |
*/ |
383 |
final val amqp_port = "amqp.port" |
384 |
|
385 |
/** |
386 |
* User name for connecting with the AMQP server |
387 |
*/ |
388 |
final val amqp_username = "amqp.username" |
389 |
|
390 |
/** |
391 |
* Passwd for connecting with the AMQP server |
392 |
*/ |
393 |
final val amqp_password = "amqp.passwd" |
394 |
|
395 |
/** |
396 |
* Virtual host on the AMQP server |
397 |
*/ |
398 |
final val amqp_vhost = "amqp.vhost" |
399 |
|
400 |
/** |
401 |
* Comma separated list of exchanges known to aquarium |
402 |
*/ |
403 |
final val amqp_exchanges = "amqp.exchanges" |
404 |
|
405 |
/** |
406 |
* REST service listening port. |
407 |
* |
408 |
* Default is 8080. |
409 |
*/ |
410 |
final val rest_port = "rest.port" |
411 |
|
412 |
/* |
413 |
* Provider for persistence services |
414 |
*/ |
415 |
final val persistence_provider = "persistence.provider" |
416 |
|
417 |
/** |
418 |
* Hostname for the persistence service |
419 |
*/ |
420 |
final val persistence_host = "persistence.host" |
421 |
|
422 |
/** |
423 |
* Username for connecting to the persistence service |
424 |
*/ |
425 |
final val persistence_username = "persistence.username" |
426 |
|
427 |
/** |
428 |
* Password for connecting to the persistence service |
429 |
*/ |
430 |
final val persistence_password = "persistence.password" |
431 |
|
432 |
/** |
433 |
* Password for connecting to the persistence service |
434 |
*/ |
435 |
final val persistence_port = "persistence.port" |
436 |
|
437 |
/** |
438 |
* The DB schema to use |
439 |
*/ |
440 |
final val persistence_db = "persistence.db" |
441 |
|
442 |
/** |
443 |
* Maximum number of open connections to MongoDB |
444 |
*/ |
445 |
final val mongo_connection_pool_size = "mongo.connection.pool.size" |
446 |
|
447 |
/** |
448 |
* Location of the Aquarium accounting policy config file |
449 |
*/ |
450 |
final val aquarium_policy = "aquarium.policy" |
451 |
|
452 |
/** |
453 |
* A time period in milliseconds for which we can tolerate stale data regarding user state. |
454 |
* |
455 |
* The smaller the value, the more accurate the user credits and other state data are. |
456 |
* |
457 |
* If a request for user state (e.g. balance) is received and the request timestamp exceeds |
458 |
* the timestamp of the last known balance amount by this value, then a re-computation for |
459 |
* the balance is triggered. |
460 |
*/ |
461 |
final val user_state_timestamp_threshold = "user.state.timestamp.threshold" |
462 |
|
463 |
/** |
464 |
* The time unit is the lowest billable time period. |
465 |
* For example, with a time unit of ten seconds, if a VM is started up and shut down in nine |
466 |
* seconds, then the user will be billed for ten seconds. |
467 |
* |
468 |
* This is an overall constant. We use it as a property in order to prepare ourselves for |
469 |
* multi-cloud setup, where the same Aquarium instance is used to bill several distinct cloud |
470 |
* infrastructures. |
471 |
*/ |
472 |
final val time_unit_in_millis = "time.unit.in.seconds" |
473 |
} |
474 |
} |