Rename Configurator to Aquarium
[aquarium] / src / main / scala / gr / grnet / aquarium / ResourceLocator.scala
1 /*
2  * Copyright 2011-2012 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 com.ckkloverdos.maybe.{Failed, Just, Maybe, NoVal}
39 import com.ckkloverdos.sys.{SysEnv, SysProp}
40 import java.io.File
41
42 import gr.grnet.aquarium.util.justForSure
43 import gr.grnet.aquarium.util.isRunningTests
44 import com.ckkloverdos.resource.{FileStreamResource, StreamResource, CompositeStreamResourceContext, ClassLoaderStreamResourceContext, FileStreamResourceContext}
45
46 /**
47  * Locates resources.
48  *
49  * This code was initially in [[gr.grnet.aquarium.Aquarium]].
50  *
51  * @author Christos KK Loverdos <loverdos@gmail.com>
52  */
53
54 object ResourceLocator {
55   final object ResourceNames {
56     final val CONF_FODLER               = "conf"
57     final val SLASH_ETC_AQUARIUM_FOLDER = "/etc/aquarium"
58
59     final val LOGBACK_XML         = "logback.xml"
60     final val AQUARIUM_PROPERTIES = "aquarium.properties"
61     final val POLICY_YAML         = "policy.yaml"
62     final val ROLE_AGREEMENTS_MAP = "roles-agreements.map"
63   }
64
65   final object Homes {
66     final object Names {
67       final val AKKA_HOME     = "AKKA_HOME"
68 //      final val AQUARIUM_HOME = "AQUARIUM_HOME"
69     }
70
71     final object Folders {
72       private[this] def checkFolder(name: String, file: File): File = {
73         if(!file.isDirectory) {
74           throw new AquariumInternalError(
75             "%s=%s is not a folder".format(
76               name,
77               if(file.isAbsolute)
78                 file.getAbsolutePath
79               else
80                 "%s [=%s]".format(file, file.getCanonicalFile)
81             )
82           )
83         }
84         file
85       }
86
87       /**
88        * This is normally exported from the shell script (AQUARIUM_HOME) that starts Aquarium or given in the command
89        * line (aquarium.home).
90        *
91        * TODO: Make this searchable for resources (ie put it in the resource context)
92        */
93       final lazy val AquariumHome = {
94         SysProps.AquariumHome.value match {
95           case Just(aquariumHome) ⇒
96             checkFolder(SysProps.Names.AquariumHome, new File(aquariumHome))
97
98           case Failed(e) ⇒
99             throw new AquariumInternalError("Error regarding %s".format(SysProps.Names.AquariumHome), e)
100
101           case NoVal ⇒
102             SysEnvs.AQUARIUM_HOME.value match {
103               case Just(aquarium_home) ⇒
104                 val folder = new File(aquarium_home)
105                 checkFolder(SysEnvs.Names.AQUARIUM_HOME, folder)
106                 SysProps.AquariumHome.update(folder.getPath) // needed for logback configuration
107                 folder
108
109               case Failed(e) ⇒
110                 throw new AquariumInternalError("Error regarding %s".format(SysEnvs.Names.AQUARIUM_HOME), e)
111
112               case NoVal ⇒
113                 val folder = new File(".")
114                 SysProps.AquariumHome.update(folder.getPath) // needed for logback configuration
115                 folder
116             }
117         }
118       }
119     }
120   }
121
122   final object SysEnvs {
123     final object Names {
124       final val AKKA_HOME     = Homes.Names.AKKA_HOME
125       final val AQUARIUM_HOME = "AQUARIUM_HOME"
126     }
127
128     final val AKKA_HOME     = SysEnv(Names.AKKA_HOME)
129     final val AQUARIUM_HOME = SysEnv(Names.AQUARIUM_HOME)
130   }
131
132   final object SysProps {
133     final object Names {
134       final val AquariumHome           = "aquarium.home"
135       final val AquariumPropertiesPath = "aquarium.properties.path"
136       final val AquariumConfFolder     = "aquarium.conf.folder"
137     }
138
139     final val AquariumHome = SysProp(Names.AquariumHome)
140
141     /**
142      * Use this property to override the place of aquarium.properties.
143      * If this is set, then it override any other way to specify the aquarium.properties location.
144      */
145     final val AquariumPropertiesPath = SysProp(Names.AquariumPropertiesPath)
146
147     /**
148      * Use this property to override the place where aquarium configuration resides.
149      *
150      * The value of this property is a folder that defines the highest-priority resource context.
151      */
152     final val AquariumConfFolder = SysProp(Names.AquariumConfFolder)
153   }
154
155   final object ResourceContexts {
156     /**
157      * AQUARIUM_HOME/conf resource context.
158      */
159     private[this] final lazy val HomeConfResourceContext = new FileStreamResourceContext(AQUARIUM_HOME_CONF_FOLDER)
160
161     /**
162      * The venerable /etc resource context. Applicable in Unix environments
163      */
164     private[this] final lazy val SlashEtcResourceContext = new FileStreamResourceContext(ResourceNames.SLASH_ETC_AQUARIUM_FOLDER)
165
166     /**
167      * Class loader resource context.
168      * This has the lowest priority.
169      */
170     private[this] final lazy val ClasspathBaseResourceContext = new ClassLoaderStreamResourceContext(
171       Thread.currentThread().getContextClassLoader)
172
173     private[this] final lazy val BasicResourceContext = new CompositeStreamResourceContext(
174       NoVal,
175       SlashEtcResourceContext,
176       HomeConfResourceContext,
177       ClasspathBaseResourceContext)
178
179     /**
180      * The resource context used in the application.
181      */
182     final lazy val MasterResourceContext = {
183       SysProps.AquariumConfFolder.value match {
184         case Just(value) ⇒
185           // We have a system override for the configuration location
186           new CompositeStreamResourceContext(
187             Just(BasicResourceContext),
188             new FileStreamResourceContext(value))
189
190         case NoVal ⇒
191           BasicResourceContext
192
193         case Failed(e) ⇒
194           throw new AquariumInternalError(e)
195       }
196     }
197   }
198
199   final lazy val AQUARIUM_HOME_CONF_FOLDER = new File(Homes.Folders.AquariumHome, ResourceNames.CONF_FODLER)
200
201   final lazy val LOGBACK_XML_FILE = new File(AQUARIUM_HOME_CONF_FOLDER, ResourceNames.LOGBACK_XML)
202
203   /**
204    * This exists in order to have a feeling of where we are.
205    */
206   final lazy val CONF_HERE = justForSure(getResource(".")).get.url.toExternalForm
207
208   final object Resources {
209     final lazy val AquariumPropertiesResource = {
210       ResourceLocator.SysProps.AquariumPropertiesPath.value match {
211         case Just(aquariumPropertiesPath) ⇒
212           // If we have a command-line override, prefer that
213           new FileStreamResource(new File(aquariumPropertiesPath))
214
215         case Failed(e) ⇒
216           // On error, fail
217           throw new AquariumInternalError(
218             "Could not find %s=%s".format(
219               ResourceLocator.SysProps.Names.AquariumPropertiesPath,
220               ResourceLocator.SysProps.Names.AquariumPropertiesPath),
221             e)
222
223         case NoVal ⇒
224           // Otherwise try other locations
225           val aquariumPropertiesRCM = ResourceLocator getResource ResourceLocator.ResourceNames.AQUARIUM_PROPERTIES
226           aquariumPropertiesRCM match {
227             case Just(aquariumProperties) ⇒
228               aquariumProperties
229
230             case NoVal ⇒
231               // No luck
232               throw new AquariumInternalError(
233                 "Could not find %s".format(ResourceLocator.ResourceNames.AQUARIUM_PROPERTIES))
234
235             case Failed(e) ⇒
236               // Bad luck
237               throw new AquariumInternalError(
238                 "Could not find %s".format(ResourceLocator.ResourceNames.AQUARIUM_PROPERTIES), e)
239           }
240       }
241     }
242   }
243
244
245   def getResource(what: String): Maybe[StreamResource] = {
246     ResourceContexts.MasterResourceContext.getResource(what)
247   }
248 }