1 /*
2  * Copyright 2004-2005 the original author or authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package grails.util;
17 
18 import groovy.lang.Closure;
19 import groovy.lang.GroovyObjectSupport;
20 import groovy.lang.MissingMethodException;
21 
22 import java.util.HashMap;
23 import java.util.Locale;
24 import java.util.Map;
25 
26 import org.springframework.util.StringUtils;
27 
28 /**
29  * An enum that represents the current environment
30  *
31  * @author Graeme Rocher
32  * @since 1.1
33  *        <p/>
34  *        Created: Dec 12, 2008
35  */
36 @SuppressWarnings("serial")
37 public enum Environment {
38 
39     /** The development environment */
40     DEVELOPMENT,
41 
42     /** The production environment */
43     PRODUCTION,
44 
45     /** The test environment */
46     TEST,
47 
48     /**
49      * For the application data source, primarly for backward compatability for those applications
50      * that use ApplicationDataSource.groovy.
51      */
52     APPLICATION,
53 
54     /** A custom environment */
55     CUSTOM;
56 
57     /**
58      * Constant used to resolve the environment via System.getProperty(Environment.KEY)
59      */
60     public static String KEY = "grails.env";
61 
62     /**
63      * Specify whether reloading is enabled for this environment
64      */
65     public static String RELOAD_ENABLED = "grails.reload.enabled";
66 
67     /**
68      * The location where to reload resources from
69      */
70     public static final String RELOAD_LOCATION = "grails.reload.location";
71 
72     /**
73      * Constants that indicates whether this GrailsApplication is running in the default environment
74      */
75     public static final String DEFAULT = "grails.env.default";
76 
77     private static final String PRODUCTION_ENV_SHORT_NAME = "prod";
78     private static final String DEVELOPMENT_ENVIRONMENT_SHORT_NAME = "dev";
79 
80     private static final String TEST_ENVIRONMENT_SHORT_NAME = "test";
81     private static Map<String, String> envNameMappings = new HashMap<String, String>() {{
82         put(DEVELOPMENT_ENVIRONMENT_SHORT_NAME, Environment.DEVELOPMENT.getName());
83         put(PRODUCTION_ENV_SHORT_NAME, Environment.PRODUCTION.getName());
84         put(TEST_ENVIRONMENT_SHORT_NAME, Environment.TEST.getName());
85     }};
86 
87     /**
88      * Returns the current environment which is typcally either DEVELOPMENT, PRODUCTION or TEST.
89      * For custom environments CUSTOM type is returned.
90      *
91      * @return The current environment.
92      */
getCurrent()93     public static Environment getCurrent() {
94 
95         String envName = System.getProperty(Environment.KEY);
96         Metadata metadata = Metadata.getCurrent();
97         if (metadata!=null && isBlank(envName)) {
98             envName = metadata.getEnvironment();
99         }
100 
101         if (isBlank(envName)) {
102             return DEVELOPMENT;
103         }
104 
105         Environment env = getEnvironment(envName);
106         if (env == null) {
107             try {
108                 env = Environment.valueOf(envName.toUpperCase());
109             }
110             catch (IllegalArgumentException e) {
111                 // ignore
112             }
113         }
114         if (env == null) {
115             env = Environment.CUSTOM;
116             env.setName(envName);
117         }
118         return env;
119     }
120 
121     /**
122      * @see #getCurrent()
123      * @return the current environment
124      */
getCurrentEnvironment()125     public static Environment getCurrentEnvironment() {
126         return getCurrent();
127     }
128 
129     /**
130      * Returns true if the application is running in development mode (within grails run-app)
131      * @return True if the application is running in development mode
132      */
isDevelopmentMode()133     public static boolean isDevelopmentMode() {
134         return getCurrent() == DEVELOPMENT && !(Metadata.getCurrent().isWarDeployed()) &&
135              System.getProperty("grails.home") != null;
136     }
137 
138     /**
139      * @return Return true if the environment has been set as a Systme property
140      */
isSystemSet()141     public static boolean isSystemSet() {
142         return System.getProperty(KEY) != null;
143     }
144 
145     /**
146      * Returns the environment for the given short name
147      * @param shortName The short name
148      * @return The Environment or null if not known
149      */
getEnvironment(String shortName)150     public static Environment getEnvironment(String shortName) {
151         final String envName = envNameMappings.get(shortName);
152         if (envName != null) {
153             return Environment.valueOf(envName.toUpperCase());
154         }
155         return null;
156     }
157 
158     /**
159      * Takes an environment specific DSL block like:
160      *
161      * <code>
162      * environments {
163      *      development {}
164      *      production {}
165      * }
166      * </code>
167      *
168      * And returns the closure that relates to the current environment
169      *
170      * @param closure The top level closure
171      * @return The environment specific block or null if non exists
172      */
getEnvironmentSpecificBlock(Closure closure)173     public static Closure getEnvironmentSpecificBlock(Closure closure) {
174         final Environment env = getCurrent();
175         return getEnvironmentSpecificBlock(env, closure);
176     }
177 
178     /**
179      * Takes an environment specific DSL block like:
180      *
181      * <code>
182      * environments {
183      *      development {}
184      *      production {}
185      * }
186      * </code>
187      *
188      * And returns the closure that relates to the specified
189      *
190      * @param env The environment to use
191      * @param closure The top level closure
192      * @return The environment specific block or null if non exists
193      */
getEnvironmentSpecificBlock(Environment env, Closure closure)194     public static Closure getEnvironmentSpecificBlock(Environment env, Closure closure) {
195         if (closure == null) {
196             return null;
197         }
198 
199         final EnvironmentBlockEvaluator evaluator = evaluateEnvironmentSpecificBlock(env, closure);
200         return evaluator.getCallable();
201     }
202 
203     /**
204      * Takes an environment specific DSL block like:
205      *
206      * <code>
207      * environments {
208      *      development {}
209      *      production {}
210      * }
211      * </code>
212      *
213      * And executes the closure that relates to the current environment
214      *
215      * @param closure The top level closure
216      * @return The result of the closure execution
217      */
executeForCurrentEnvironment(Closure closure)218     public static Object executeForCurrentEnvironment(Closure closure) {
219         final Environment env = getCurrent();
220         return executeForEnvironment(env, closure);
221     }
222 
223     /**
224      * Takes an environment specific DSL block like:
225      *
226      * <code>
227      * environments {
228      *      development {}
229      *      production {}
230      * }
231      * </code>
232      *
233      * And executes the closure that relates to the specified environment
234      *
235      * @param env The environment to use
236      * @param closure The top level closure
237      * @return The result of the closure execution
238      */
executeForEnvironment(Environment env, Closure closure)239     public static Object executeForEnvironment(Environment env, Closure closure) {
240         if (closure == null) {
241             return null;
242         }
243 
244         final EnvironmentBlockEvaluator evaluator = evaluateEnvironmentSpecificBlock(env, closure);
245         return evaluator.execute();
246     }
247 
evaluateEnvironmentSpecificBlock(Environment environment, Closure closure)248     private static EnvironmentBlockEvaluator evaluateEnvironmentSpecificBlock(Environment environment, Closure closure) {
249         final EnvironmentBlockEvaluator evaluator = new EnvironmentBlockEvaluator(environment);
250         closure.setDelegate(evaluator);
251         closure.call();
252         return evaluator;
253     }
254 
255     private static class EnvironmentBlockEvaluator extends GroovyObjectSupport {
256         private Environment current;
257         private Closure callable;
258 
getCallable()259         public Closure getCallable() {
260             return callable;
261         }
262 
execute()263         Object execute() {
264             return callable == null ? null : callable.call();
265         }
266 
EnvironmentBlockEvaluator(Environment e)267         private EnvironmentBlockEvaluator(Environment e) {
268             this.current = e;
269         }
270 
271         @SuppressWarnings("unused")
environments(Closure c)272         public void environments(Closure c) {
273             if (c != null) {
274                 c.setDelegate(this);
275                 c.call();
276             }
277         }
278         @SuppressWarnings("unused")
production(Closure c)279         public void production(Closure c) {
280             if (current == Environment.PRODUCTION) {
281                 this.callable = c;
282             }
283         }
284         @SuppressWarnings("unused")
development(Closure c)285         public void development(Closure c) {
286             if (current == Environment.DEVELOPMENT) {
287                 this.callable = c;
288             }
289         }
290         @SuppressWarnings("unused")
test(Closure c)291         public void test(Closure c) {
292             if (current == Environment.TEST) {
293                 this.callable = c;
294             }
295         }
296 
297         @SuppressWarnings("unused")
methodMissing(String name, Object args)298         public Object methodMissing(String name, Object args) {
299             Object[] argsArray = (Object[])args;
300             if (args != null && argsArray.length > 0 && (argsArray[0] instanceof Closure)) {
301                 if (current == Environment.CUSTOM && current.getName().equals(name)) {
302                     this.callable = (Closure) argsArray[0];
303                 }
304                 return null;
305             }
306             throw new MissingMethodException(name, Environment.class, argsArray);
307         }
308     }
309 
isBlank(String value)310     private static boolean isBlank(String value) {
311         return value == null || value.trim().length() == 0;
312     }
313 
314     private String name;
315 
316     /**
317      * @return  the name of the environment
318      */
getName()319     public String getName() {
320         if (name == null) {
321             return this.toString().toLowerCase(Locale.getDefault());
322         }
323         return name;
324     }
325 
326     /**
327      * Set the name.
328      * @param name  the name
329      */
setName(String name)330     public void setName(String name) {
331         this.name = name;
332     }
333 
334     /**
335      * @return Returns whether reload is enabled for the environment
336      */
isReloadEnabled()337     public boolean isReloadEnabled() {
338         final boolean reloadOverride = Boolean.getBoolean(RELOAD_ENABLED);
339 
340         final String reloadLocation = getReloadLocationInternal();
341         final boolean reloadLocationSpecified = StringUtils.hasLength(reloadLocation);
342         return this == DEVELOPMENT && reloadLocationSpecified && !Metadata.getCurrent().isWarDeployed() ||
343                 reloadOverride && reloadLocationSpecified;
344     }
345 
346     /**
347      * @return Obtains the location to reload resources from
348      */
getReloadLocation()349     public String getReloadLocation() {
350         String location = getReloadLocationInternal();
351         if (StringUtils.hasLength(location)) {
352             return location;
353         }
354         return "."; // default to the current directory
355     }
356 
357     /**
358      * @return Whether a reload location is specified
359      */
hasReloadLocation()360     public boolean hasReloadLocation() {
361         return StringUtils.hasLength(getReloadLocationInternal());
362     }
363 
getReloadLocationInternal()364     private String getReloadLocationInternal() {
365         String location = System.getProperty(RELOAD_LOCATION);
366         if (!StringUtils.hasLength(location)) location = System.getProperty(BuildSettings.APP_BASE_DIR);
367         if (!StringUtils.hasLength(location)) {
368             BuildSettings settings = BuildSettingsHolder.getSettings();
369             if (settings != null) {
370                 location = settings.getBaseDir().getAbsolutePath();
371             }
372         }
373         return location;
374     }
375 }
376