2 * Copyright 2011-2012 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 com.ckkloverdos.key.{BooleanKey, TypedKey}
39 import com.ckkloverdos.env.Env
40 import com.ckkloverdos.props.Props
41 import com.ckkloverdos.maybe.{MaybeOption, Failed, MaybeEither, Just, NoVal}
42 import gr.grnet.aquarium.util.Loggable
44 import gr.grnet.aquarium.store.StoreProvider
45 import gr.grnet.aquarium.logic.accounting.algorithm.SimpleCostPolicyAlgorithmCompiler
46 import gr.grnet.aquarium.computation.UserStateComputations
47 import gr.grnet.aquarium.service.{StoreWatcherService, RabbitMQService, AkkaService, SimpleTimerService, EventBusService}
48 import gr.grnet.aquarium.converter.StdConverters
49 import gr.grnet.aquarium.service.event.AquariumCreatedEvent
52 * Create a tailored Aquarium.
56 * @author Christos KK Loverdos <loverdos@gmail.com>
59 final class AquariumBuilder(val originalProps: Props) extends Loggable {
60 if(originalProps eq null) {
61 throw new AquariumInternalError("props is null")
64 import Aquarium.EnvKeys
66 private[this] var _env = Env()
68 private[this] val eventBus = new EventBusService
71 private[this] var _aquarium: Aquarium = _
73 @throws(classOf[AquariumInternalError])
74 private def propsGetEx(key: String): String = {
76 originalProps.getEx(key)
79 throw new AquariumInternalError("Could not locate %s in Aquarium properties".format(key))
83 @throws(classOf[AquariumInternalError])
84 private def envGetEx[T: Manifest](key: TypedKey[T]): T = {
89 throw new AquariumInternalError("Could not locate %s in Aquarium environment".format(key))
93 def update[T: Manifest](keyvalue: (TypedKey[T], T)): this.type = {
94 assert(keyvalue ne null, "keyvalue ne null")
100 def update[T : Manifest](key: TypedKey[T], value: T): this.type = {
101 assert(key ne null, "key ne null")
103 this update (key -> value)
107 * Reflectively provide a new instance of a class and configure it appropriately.
109 private[this] def newInstance[C <: AnyRef](manifest: Manifest[C], className: String): C = {
110 val defaultClassLoader = Thread.currentThread().getContextClassLoader
111 val instanceM = MaybeEither(defaultClassLoader.loadClass(className).newInstance().asInstanceOf[C])
113 case Just(instance) ⇒
114 eventBus.addSubscriber(instance)
117 case configurable: Configurable if (originalProps ne null) ⇒
118 val localProps = configurable.propertyPrefix match {
119 case somePrefix @ Some(prefix) ⇒
120 if(prefix.length == 0) {
122 "Property prefix for %s is %s. Consider using None".format(instance, somePrefix))
125 originalProps.subsetForKeyPrefix(prefix)
131 logger.debug("Configuring {} with props", configurable.getClass.getName)
132 MaybeEither(configurable configure localProps) match {
134 logger.info("Configured {} with props", configurable.getClass.getName)
138 throw new AquariumInternalError("Could not configure instance of %s".format(className), e)
146 throw new AquariumInternalError("Could not instantiate %s".format(className), e)
150 private[this] def checkStoreProviderOverride: Unit = {
151 val envKey = EnvKeys.storeProvider
152 if(_env.contains(envKey)) {
156 if(originalProps eq null) {
157 throw new AquariumInternalError("Cannot locate store provider, since no properties have been defined")
160 val propName = envKey.name
161 originalProps.get(propName) match {
162 case Just(propValue) ⇒
163 update(envKey, newInstance(envKey.keyType, propValue))
166 throw new AquariumInternalError("No store provider is given in properties")
169 throw new AquariumInternalError(e, "While obtaining value for key %s in properties".format(propName))
173 private[this] def checkStoreOverrides: Unit = {
174 if(originalProps eq null) {
178 def checkOverride[S <: AnyRef : Manifest](envKey: TypedKey[S], f: StoreProvider ⇒ S): Unit = {
179 if(!_env.contains(envKey)) {
180 val propName = envKey.name
182 originalProps.get(propName) match {
183 case Just(propValue) ⇒
184 // Create the store reflectively
185 update(envKey, newInstance(envKey.keyType, propValue))
188 // Get the store from the store provider
189 val storeProvider = this.envGetEx(EnvKeys.storeProvider)
190 val propValue = f(storeProvider)
191 update(envKey, propValue)
194 throw new AquariumInternalError(e, "While obtaining value for key %s in properties".format(propName))
199 // If a store has not been specifically overridden, we load it from the properties
200 checkOverride(EnvKeys.resourceEventStore, _.resourceEventStore)
201 checkOverride(EnvKeys.imEventStore, _.imEventStore)
202 checkOverride(EnvKeys.userStateStore, _.userStateStore)
203 checkOverride(EnvKeys.policyStore, _.policyStore)
206 private[this] def checkEventsStoreFolderOverride: Unit = {
207 val propName = EnvKeys.eventsStoreFolder.name
209 _env.get(EnvKeys.eventsStoreFolder) match {
210 case Just(storeFolderOption) ⇒
211 // Some value has been set, even a None, so do nothing more
212 logger.info("{} = {}", propName, storeFolderOption)
215 throw new AquariumInternalError(e, "While obtaining value for env key %s".format(propName))
218 if(originalProps eq null) {
219 update(EnvKeys.eventsStoreFolder, None)
224 for(folderName ← originalProps.get(propName)) {
225 logger.info("{} = {}", propName, folderName)
227 update(EnvKeys.eventsStoreFolder, Some(new File(folderName)))
233 private[this] def checkEventsStoreFolderExistence: Unit = {
234 val propName = EnvKeys.eventsStoreFolder.name
235 for(folder ← this.envGetEx(EnvKeys.eventsStoreFolder)) {
236 val canonicalFolder = {
237 if(folder.isAbsolute) {
238 folder.getCanonicalFile
240 logger.info("{} is not absolute, making it relative to Aquarium Home", propName)
241 new File(ResourceLocator.Homes.Folders.AquariumHome, folder.getPath).getCanonicalFile
245 val canonicalPath = canonicalFolder.getCanonicalPath
247 if(canonicalFolder.exists() && !canonicalFolder.isDirectory) {
248 throw new AquariumInternalError("%s = %s is not a folder".format(propName, canonicalFolder))
251 // Now, events folder must be outside AQUARIUM_HOME, since AQUARIUM_HOME can be wiped out for an upgrade but
252 // we still want to keep the events.
253 val ahCanonicalPath = ResourceLocator.Homes.Folders.AquariumHome.getCanonicalPath
254 if(canonicalPath.startsWith(ahCanonicalPath)) {
255 throw new AquariumInternalError(
256 "%s = %s is under Aquarium Home = %s".format(
263 canonicalFolder.mkdirs()
265 update(EnvKeys.eventsStoreFolder, Some(canonicalFolder))
269 private[this] def checkEventsStoreFolderVariablesOverrides: Unit = {
270 def checkVar(envKey: BooleanKey): Unit = {
271 if(!_env.contains(envKey)) {
272 val propName = envKey.name
273 originalProps.getBoolean(propName) match {
274 case Just(propValue) ⇒
275 update(envKey, propValue)
278 update(envKey, false)
281 throw new AquariumInternalError(e, "While obtaining value for key %s in properties".format(propName))
286 checkVar(EnvKeys.eventsStoreSaveRCEvents)
287 checkVar(EnvKeys.eventsStoreSaveIMEvents)
290 private[this] def checkRestServiceOverride: Unit = {
291 checkNoPropsOverride(EnvKeys.restService) { envKey ⇒
292 val envKey = EnvKeys.restService
293 val propName = envKey.name
294 val propValue = propsGetEx(propName)
296 newInstance(envKey.keyType, propValue)
300 private[this] def checkNoPropsOverride[T: Manifest](envKey: TypedKey[T])(f: TypedKey[T] ⇒ T): Unit = {
301 if(_env.contains(envKey)) {
305 update(envKey, f(envKey))
308 private[this] def checkPropsOverride[T: Manifest](envKey: TypedKey[T])(f: (TypedKey[T], String) ⇒ T): Unit = {
309 if(_env.contains(envKey)) {
313 val propName = envKey.name
314 originalProps.get(propName) match {
315 case Just(propValue) ⇒
316 update(envKey, f(envKey, propValue))
319 throw new AquariumInternalError("No value for key %s in properties".format(propName))
322 throw new AquariumInternalError(e, "While obtaining value for key %s in properties".format(propName))
326 private[this] def checkOptionalPropsOverride[T: Manifest]
327 (envKey: TypedKey[Option[T]])
328 (f: (TypedKey[Option[T]], String) ⇒ Option[T]): Unit = {
330 if(_env.contains(envKey)) {
334 val propName = envKey.name
335 originalProps.get(propName) match {
336 case Just(propValue) ⇒
337 update(envKey, f(envKey, propValue))
343 throw new AquariumInternalError(e, "While obtaining value for key %s in properties".format(propName))
347 def build(): Aquarium = {
348 if(this._aquarium ne null) {
349 return this._aquarium
352 checkPropsOverride(EnvKeys.version) { (envKey, propValue) ⇒ propValue }
354 checkNoPropsOverride(EnvKeys.eventBus) { _ ⇒ eventBus }
356 checkNoPropsOverride(EnvKeys.originalProps) { _ ⇒ originalProps }
358 checkNoPropsOverride(EnvKeys.defaultClassLoader) { _ ⇒ Thread.currentThread().getContextClassLoader }
360 checkNoPropsOverride(EnvKeys.converters) { _ ⇒ StdConverters.AllConverters }
362 checkStoreProviderOverride
365 checkEventsStoreFolderOverride
366 checkEventsStoreFolderExistence
367 checkEventsStoreFolderVariablesOverrides
369 checkRestServiceOverride
371 checkNoPropsOverride(EnvKeys.timerService) { envKey ⇒
372 newInstance(envKey.keyType, classOf[SimpleTimerService].getName)
375 checkNoPropsOverride(EnvKeys.algorithmCompiler) { _ ⇒ SimpleCostPolicyAlgorithmCompiler }
377 checkNoPropsOverride(EnvKeys.userStateComputations) { envKey ⇒
378 newInstance(envKey.keyType, classOf[UserStateComputations].getName)
381 checkNoPropsOverride(EnvKeys.akkaService) { envKey ⇒
382 newInstance(envKey.keyType, classOf[AkkaService].getName)
385 checkNoPropsOverride(EnvKeys.rabbitMQService) { envKey ⇒
386 newInstance(envKey.keyType, classOf[RabbitMQService].getName)
389 checkNoPropsOverride(EnvKeys.storeWatcherService) { envKey ⇒
390 newInstance(envKey.keyType, classOf[StoreWatcherService].getName)
393 checkPropsOverride(EnvKeys.actorProvider) { (envKey, propValue) ⇒
394 newInstance(envKey.keyType, propValue)
397 checkPropsOverride(EnvKeys.userStateTimestampThreshold) { (envKey, propValue) ⇒
401 checkPropsOverride(EnvKeys.restPort) { (envKey, propValue) ⇒
405 checkOptionalPropsOverride(EnvKeys.adminCookie) { (envKey, propValue) ⇒
409 this._aquarium = new Aquarium(_env)
411 this._aquarium.eventBus.syncPost(AquariumCreatedEvent(this._aquarium))