4f4bb44c5ee0f1b3a8df1020fb311c9c15bdb677
[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 import com.ckkloverdos.props.Props
46 import com.ckkloverdos.convert.Converters._
47 import com.ckkloverdos.maybe.Just
48 import com.ckkloverdos.maybe.Failed
49 import com.ckkloverdos.convert.Converters
50
51 /**
52  * Locates resources.
53  *
54  * This code was initially in [[gr.grnet.aquarium.Aquarium]].
55  *
56  * @author Christos KK Loverdos <loverdos@gmail.com>
57  */
58
59 object ResourceLocator {
60   final object ResourceNames {
61     final val CONF_FODLER               = "conf"
62     final val SLASH_ETC_AQUARIUM_FOLDER = "/etc/aquarium"
63
64     final val LOGBACK_XML         = "logback.xml"
65     final val AQUARIUM_PROPERTIES = "aquarium.properties"
66     final val POLICY_YAML         = "policy.yaml"
67     final val ROLE_AGREEMENTS_MAP = "roles-agreements.map"
68   }
69
70   final object Homes {
71     final object Names {
72       final val AKKA_HOME     = "AKKA_HOME"
73 //      final val AQUARIUM_HOME = "AQUARIUM_HOME"
74     }
75
76     final object Folders {
77       private[this] def checkFolder(name: String, file: File): File = {
78         if(!file.isDirectory) {
79           throw new AquariumInternalError(
80             "%s=%s is not a folder".format(
81               name,
82               if(file.isAbsolute)
83                 file.getAbsolutePath
84               else
85                 "%s [=%s]".format(file, file.getCanonicalFile)
86             )
87           )
88         }
89         file
90       }
91
92       /**
93        * This is normally exported from the shell script (AQUARIUM_HOME) that starts Aquarium or given in the command
94        * line (aquarium.home).
95        *
96        * TODO: Make this searchable for resources (ie put it in the resource context)
97        */
98       final lazy val AquariumHome = {
99         SysProps.AquariumHome.value match {
100           case Just(aquariumHome) ⇒
101             checkFolder(SysProps.Names.AquariumHome, new File(aquariumHome))
102
103           case Failed(e) ⇒
104             throw new AquariumInternalError("Error regarding %s".format(SysProps.Names.AquariumHome), e)
105
106           case NoVal ⇒
107             SysEnvs.AQUARIUM_HOME.value match {
108               case Just(aquarium_home) ⇒
109                 val folder = new File(aquarium_home)
110                 checkFolder(SysEnvs.Names.AQUARIUM_HOME, folder)
111                 SysProps.AquariumHome.update(folder.getPath) // needed for logback configuration
112                 folder
113
114               case Failed(e) ⇒
115                 throw new AquariumInternalError("Error regarding %s".format(SysEnvs.Names.AQUARIUM_HOME), e)
116
117               case NoVal ⇒
118                 val folder = new File(".")
119                 SysProps.AquariumHome.update(folder.getPath) // needed for logback configuration
120                 folder
121             }
122         }
123       }
124     }
125   }
126
127   final object SysEnvs {
128     final object Names {
129       final val AKKA_HOME     = Homes.Names.AKKA_HOME
130       final val AQUARIUM_HOME = "AQUARIUM_HOME"
131     }
132
133     final val AKKA_HOME     = SysEnv(Names.AKKA_HOME)
134     final val AQUARIUM_HOME = SysEnv(Names.AQUARIUM_HOME)
135   }
136
137   final object SysProps {
138     final object Names {
139       final val AquariumHome           = "aquarium.home"
140       final val AquariumPropertiesPath = "aquarium.properties.path"
141       final val AquariumConfFolder     = "aquarium.conf.folder"
142     }
143
144     final val AquariumHome = SysProp(Names.AquariumHome)
145
146     /**
147      * Use this property to override the place of aquarium.properties.
148      * If this is set, then it override any other way to specify the aquarium.properties location.
149      */
150     final val AquariumPropertiesPath = SysProp(Names.AquariumPropertiesPath)
151
152     /**
153      * Use this property to override the place where aquarium configuration resides.
154      *
155      * The value of this property is a folder that defines the highest-priority resource context.
156      */
157     final val AquariumConfFolder = SysProp(Names.AquariumConfFolder)
158   }
159
160   final object ResourceContexts {
161     /**
162      * AQUARIUM_HOME/conf resource context.
163      */
164     private[this] final lazy val HomeConfResourceContext = new FileStreamResourceContext(AQUARIUM_HOME_CONF_FOLDER)
165
166     /**
167      * The venerable /etc resource context. Applicable in Unix environments
168      */
169     private[this] final lazy val SlashEtcResourceContext = new FileStreamResourceContext(ResourceNames.SLASH_ETC_AQUARIUM_FOLDER)
170
171     /**
172      * Class loader resource context.
173      * This has the lowest priority.
174      */
175     private[this] final lazy val ClasspathBaseResourceContext = new ClassLoaderStreamResourceContext(
176       Thread.currentThread().getContextClassLoader)
177
178     private[this] final lazy val BasicResourceContext = new CompositeStreamResourceContext(
179       NoVal,
180       SlashEtcResourceContext,
181       HomeConfResourceContext,
182       ClasspathBaseResourceContext)
183
184     /**
185      * The resource context used in the application.
186      */
187     final lazy val MasterResourceContext = {
188       SysProps.AquariumConfFolder.value match {
189         case Just(value) ⇒
190           // We have a system override for the configuration location
191           new CompositeStreamResourceContext(
192             Just(BasicResourceContext),
193             new FileStreamResourceContext(value))
194
195         case NoVal ⇒
196           BasicResourceContext
197
198         case Failed(e) ⇒
199           throw new AquariumInternalError(e)
200       }
201     }
202   }
203
204   final lazy val AQUARIUM_HOME_CONF_FOLDER = new File(Homes.Folders.AquariumHome, ResourceNames.CONF_FODLER)
205
206   /**
207    * This exists in order to have a feeling of where we are.
208    */
209   final lazy val HERE = justForSure(getResource(".")).get.url.toExternalForm
210
211   final object Resources {
212     final lazy val AquariumPropertiesResource = {
213       ResourceLocator.SysProps.AquariumPropertiesPath.value match {
214         case Just(aquariumPropertiesPath) ⇒
215           // If we have a command-line override, prefer that
216           new FileStreamResource(new File(aquariumPropertiesPath))
217
218         case Failed(e) ⇒
219           // On error, fail
220           throw new AquariumInternalError(
221             "Could not find %s".format(ResourceLocator.SysProps.Names.AquariumPropertiesPath), 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     final lazy val LogbackXMLResource = {
244       getResource(ResourceNames.LOGBACK_XML) match {
245         case Just(logbackXML) ⇒
246           logbackXML
247
248         case NoVal ⇒
249           throw new AquariumInternalError(
250             "Could not find %s".format(ResourceLocator.ResourceNames.LOGBACK_XML))
251
252         case Failed(e) ⇒
253           throw new AquariumInternalError(
254             "Could not find %s".format(ResourceLocator.ResourceNames.LOGBACK_XML), e)
255
256       }
257     }
258
259     final lazy val PolicyYAMLResource = {
260       ResourceLocator.getResource(ResourceLocator.ResourceNames.POLICY_YAML) match {
261         case Just(policyYAML) ⇒
262           policyYAML
263
264         case NoVal ⇒
265           throw new AquariumInternalError(
266             "Could not find %s".format(ResourceLocator.ResourceNames.POLICY_YAML))
267
268         case Failed(e) ⇒
269           throw new AquariumInternalError(
270             "Could not find %s".format(ResourceLocator.ResourceNames.POLICY_YAML), e)
271       }
272     }
273   }
274
275   final lazy val AquariumProperties = {
276     implicit val DefaultConverters = Converters.DefaultConverters
277     val maybeProps = Props(Resources.AquariumPropertiesResource)
278     maybeProps match {
279       case Just(props) ⇒
280         props
281
282       case NoVal ⇒
283         throw new AquariumInternalError(
284           "Could not load %s from %s".format(
285             ResourceLocator.ResourceNames.AQUARIUM_PROPERTIES,
286             Resources.AquariumPropertiesResource))
287
288
289       case Failed(e) ⇒
290         throw new AquariumInternalError(
291           "Could not load %s from %s".format(
292             ResourceLocator.ResourceNames.AQUARIUM_PROPERTIES,
293             Resources.AquariumPropertiesResource),
294           e)
295     }
296   }
297
298
299   def getResource(what: String): Maybe[StreamResource] = {
300     ResourceContexts.MasterResourceContext.getResource(what)
301   }
302 }