/* * Copyright 2004-2005 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package grails.util; import grails.io.IOUtils; import groovy.lang.Closure; import groovy.lang.GroovyObjectSupport; import groovy.lang.MissingMethodException; import org.codehaus.groovy.control.MultipleCompilationErrorsException; import org.codehaus.groovy.runtime.DefaultGroovyMethods; import org.grails.io.support.Resource; import org.grails.io.support.UrlResource; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.Locale; import java.util.Map; import java.util.jar.Attributes; import java.util.jar.Manifest; /** * Represents the current environment. * * @author Graeme Rocher * @since 1.1 */ public enum Environment { /** The development environment */ DEVELOPMENT, /** The production environment */ PRODUCTION, /** The test environment */ TEST, /** * For the application data source, primarly for backward compatability for those applications * that use ApplicationDataSource.groovy. */ APPLICATION, /** A custom environment */ CUSTOM; /** * Constant used to resolve the environment via System.getProperty(Environment.KEY) */ public static String KEY = "grails.env"; /** * The name of the GRAILS_HOME environment variable */ public static String ENV_GRAILS_HOME = "GRAILS_HOME"; /** * Specify whether reloading is enabled for this environment */ public static String RELOAD_ENABLED = "grails.reload.enabled"; /** * Constant indicating whether run-app or test-app was executed */ public static String RUN_ACTIVE = "grails.run.active"; /** * Whether the display of full stack traces is needed */ public static String FULL_STACKTRACE = "grails.full.stacktrace"; /** * The location where to reload resources from */ public static final String RELOAD_LOCATION = "grails.reload.location"; /** * Whether interactive mode is enabled */ public static final String INTERACTIVE_MODE_ENABLED = "grails.interactive.mode.enabled"; /** * Constants that indicates whether this GrailsApplication is running in the default environment */ public static final String DEFAULT = "grails.env.default"; /** * Whether Grails is in the middle of bootstrapping or not */ public static final String INITIALIZING = "grails.env.initializing"; /** * Whether Grails has been executed standalone via the static void main method and not loaded in via the container */ public static final String STANDALONE = "grails.env.standalone"; private static final String PRODUCTION_ENV_SHORT_NAME = "prod"; private static final String DEVELOPMENT_ENVIRONMENT_SHORT_NAME = "dev"; private static final String TEST_ENVIRONMENT_SHORT_NAME = "test"; @SuppressWarnings("unchecked") private static Map<String, String> envNameMappings = CollectionUtils.<String, String>newMap( DEVELOPMENT_ENVIRONMENT_SHORT_NAME, Environment.DEVELOPMENT.getName(), PRODUCTION_ENV_SHORT_NAME, Environment.PRODUCTION.getName(), TEST_ENVIRONMENT_SHORT_NAME, Environment.TEST.getName()); private static Holder<Environment> cachedCurrentEnvironment = new Holder<>("Environment"); private static final boolean DEVELOPMENT_MODE = getCurrent() == DEVELOPMENT && BuildSettings.GRAILS_APP_DIR_PRESENT; private static boolean initializingState = false; private static final String GRAILS_IMPLEMENTATION_TITLE = "Grails"; private static final String GRAILS_VERSION; private static final boolean STANDALONE_DEPLOYED; private static final boolean WAR_DEPLOYED; static { Package p = Environment.class.getPackage(); String version = p != null ? p.getImplementationVersion() : null; if (version == null || isBlank(version)) { try { URL manifestURL = IOUtils.findResourceRelativeToClass(Environment.class, "/META-INF/MANIFEST.MF"); Manifest grailsManifest = null; if(manifestURL != null) { Resource r = new UrlResource(manifestURL); if(r.exists()) { InputStream inputStream = null; Manifest mf = null; try { inputStream = r.getInputStream(); mf = new Manifest(inputStream); } finally { try { inputStream.close(); } catch (IOException e) { // ignore } } String implTitle = mf.getMainAttributes().getValue(Attributes.Name.IMPLEMENTATION_TITLE); if (!isBlank(implTitle) && implTitle.equals(GRAILS_IMPLEMENTATION_TITLE)) { grailsManifest = mf; } } } if (grailsManifest != null) { version = grailsManifest.getMainAttributes().getValue(Attributes.Name.IMPLEMENTATION_VERSION); } if (isBlank(version)) { version = "Unknown"; } } catch (Exception e) { version = "Unknown"; } } GRAILS_VERSION = version; URL url = Environment.class.getResource(""); if(url != null) { String protocol = url.getProtocol(); if(protocol.equals("jar")) { String fullPath = url.toString(); if(fullPath.contains(IOUtils.RESOURCE_WAR_PREFIX)) { STANDALONE_DEPLOYED = true; } else { int i = fullPath.indexOf(IOUtils.RESOURCE_JAR_PREFIX); if(i > -1) { fullPath = fullPath.substring(i + IOUtils.RESOURCE_JAR_PREFIX.length()); STANDALONE_DEPLOYED = fullPath.contains(IOUtils.RESOURCE_JAR_PREFIX); } else { STANDALONE_DEPLOYED = false; } } } else { STANDALONE_DEPLOYED = false; } } else { STANDALONE_DEPLOYED = false; } URL loadedLocation = Environment.class.getClassLoader().getResource(Metadata.FILE); if(loadedLocation != null ) { String path = loadedLocation.getPath(); WAR_DEPLOYED = isWebPath(path); } else { loadedLocation = Thread.currentThread().getContextClassLoader().getResource(Metadata.FILE); if(loadedLocation != null ) { String path = loadedLocation.getPath(); WAR_DEPLOYED = isWebPath(path); } else { WAR_DEPLOYED = false; } } } public static Throwable currentReloadError = null; public static MultipleCompilationErrorsException currentCompilationError = null; private String name; private String reloadLocation; Environment() { initialize(); } /** * @return The current Grails version */ public static String getGrailsVersion() { return GRAILS_VERSION; } public static void setCurrentReloadError(Throwable currentReloadError) { Environment.currentReloadError = currentReloadError; } public static MultipleCompilationErrorsException getCurrentCompilationError() { return currentCompilationError; } public static Throwable getCurrentReloadError() { return currentReloadError; } public static boolean isReloadInProgress() { return Boolean.getBoolean("grails.reloading.in.progress"); } private void initialize() { name = toString().toLowerCase(Locale.ENGLISH); } /** * Returns the current environment which is typcally either DEVELOPMENT, PRODUCTION or TEST. * For custom environments CUSTOM type is returned. * * @return The current environment. */ public static Environment getCurrent() { String envName = System.getProperty(Environment.KEY); Environment env; if(!isBlank(envName)) { env = getEnvironment(envName); if(env != null) { return env; } } Environment current = cachedCurrentEnvironment.get(); if (current != null) { return current; } return cacheCurrentEnvironment(); } private static Environment resolveCurrentEnvironment() { String envName = System.getProperty(Environment.KEY); if (isBlank(envName)) { Metadata metadata = Metadata.getCurrent(); if (metadata != null) { envName = metadata.getEnvironment(); } if (isBlank(envName)) { return DEVELOPMENT; } } Environment env = getEnvironment(envName); if (env == null) { try { env = Environment.valueOf(envName.toUpperCase()); } catch (IllegalArgumentException e) { // ignore } } if (env == null) { env = Environment.CUSTOM; env.setName(envName); } return env; } private static Environment cacheCurrentEnvironment() { Environment env = resolveCurrentEnvironment(); cachedCurrentEnvironment.set(env); return env; } /** * @see #getCurrent() * @return the current environment */ public static Environment getCurrentEnvironment() { return getCurrent(); } /** * Reset the current environment */ public static void reset() { cachedCurrentEnvironment.set(null); Metadata.reset(); } /** * Returns true if the application is running in development mode (within grails run-app) * * @return true if the application is running in development mode */ public static boolean isDevelopmentMode() { return DEVELOPMENT_MODE; } /** * This method will return true if the 'grails-app' directory was found, regardless of whether reloading is active or not * * @return True if the development sources are present */ public static boolean isDevelopmentEnvironmentAvailable() { return BuildSettings.GRAILS_APP_DIR_PRESENT && !isStandaloneDeployed() && !isWarDeployed(); } /** * This method will return true the application is run * * @return True if the development sources are present */ public static boolean isDevelopmentRun() { Environment env = Environment.getCurrent(); return isDevelopmentEnvironmentAvailable() && Boolean.getBoolean(RUN_ACTIVE) && (env == Environment.DEVELOPMENT); } /** * Check whether the application is deployed * @return true if is */ public static boolean isWarDeployed() { if(!isStandalone()) { return WAR_DEPLOYED; } return false; } private static boolean isWebPath(String path) { // Workaround for weblogic who repacks files from 'classes' into a new jar under lib/ return path.contains("/WEB-INF/classes") || path.contains("_wl_cls_gen.jar!/"); } /** * Whether the application has been executed standalone via static void main. * * This method will return true when the application is executed via `java -jar` or * if the application is run directly via the main method within an IDE * * @return True if it is running standalone outside of a servlet container */ public static boolean isStandalone() { return Boolean.getBoolean(STANDALONE); } /** * Whether the application is running standalone within a JAR * * This method will return true only if the the application is executed via `java -jar` * and not if it is run via the main method within an IDE * * @return True if it is running standalone outside a servlet container from within a JAR or WAR file */ public static boolean isStandaloneDeployed() { return isStandalone() && STANDALONE_DEPLOYED; } /** * Whether this is a fork of the Grails command line environment * @return True if it is a fork */ public static boolean isFork() { return Boolean.getBoolean("grails.fork.active"); } /** * Returns whether the environment is running within the Grails shell (executed via the 'grails' command line in a terminal window) * @return true if is */ public static boolean isWithinShell() { return DefaultGroovyMethods.getRootLoader(Environment.class.getClassLoader()) != null; } /** * @return Return true if the environment has been set as a System property */ public static boolean isSystemSet() { return System.getProperty(KEY) != null; } /** * Returns the environment for the given short name * @param shortName The short name * @return The Environment or null if not known */ public static Environment getEnvironment(String shortName) { final String envName = envNameMappings.get(shortName); if (envName != null) { return Environment.valueOf(envName.toUpperCase()); } else { try { return Environment.valueOf(shortName.toUpperCase()); } catch ( IllegalArgumentException ise ) { return null; } } } /** * Takes an environment specific DSL block like: * * <code> * environments { * development {} * production {} * } * </code> * * And returns the closure that relates to the current environment * * @param closure The top level closure * @return The environment specific block or null if non exists */ public static Closure<?> getEnvironmentSpecificBlock(Closure<?> closure) { final Environment env = getCurrent(); return getEnvironmentSpecificBlock(env, closure); } /** * Takes an environment specific DSL block like: * * <code> * environments { * development {} * production {} * } * </code> * * And returns the closure that relates to the specified * * @param env The environment to use * @param closure The top level closure * @return The environment specific block or null if non exists */ public static Closure<?> getEnvironmentSpecificBlock(Environment env, Closure<?> closure) { if (closure == null) { return null; } final EnvironmentBlockEvaluator evaluator = evaluateEnvironmentSpecificBlock(env, closure); return evaluator.getCallable(); } /** * Takes an environment specific DSL block like: * * <code> * environments { * development {} * production {} * } * </code> * * And executes the closure that relates to the current environment * * @param closure The top level closure * @return The result of the closure execution */ public static Object executeForCurrentEnvironment(Closure<?> closure) { final Environment env = getCurrent(); return executeForEnvironment(env, closure); } /** * Takes an environment specific DSL block like: * * <code> * environments { * development {} * production {} * } * </code> * * And executes the closure that relates to the specified environment * * @param env The environment to use * @param closure The top level closure * @return The result of the closure execution */ public static Object executeForEnvironment(Environment env, Closure<?> closure) { if (closure == null) { return null; } final EnvironmentBlockEvaluator evaluator = evaluateEnvironmentSpecificBlock(env, closure); return evaluator.execute(); } private static EnvironmentBlockEvaluator evaluateEnvironmentSpecificBlock(Environment environment, Closure<?> closure) { final EnvironmentBlockEvaluator evaluator = new EnvironmentBlockEvaluator(environment); closure.setDelegate(evaluator); closure.call(); return evaluator; } private static class EnvironmentBlockEvaluator extends GroovyObjectSupport { private Environment current; private Closure<?> callable; public Closure<?> getCallable() { return callable; } Object execute() { return callable == null ? null : callable.call(); } private EnvironmentBlockEvaluator(Environment e) { current = e; } @SuppressWarnings("unused") public void environments(Closure<?> c) { if (c != null) { c.setDelegate(this); c.call(); } } @SuppressWarnings("unused") public void production(Closure<?> c) { if (current == Environment.PRODUCTION) { callable = c; } } @SuppressWarnings("unused") public void development(Closure<?> c) { if (current == Environment.DEVELOPMENT) { callable = c; } } @SuppressWarnings("unused") public void test(Closure<?> c) { if (current == Environment.TEST) { callable = c; } } @SuppressWarnings("unused") public Object methodMissing(String name, Object args) { Object[] argsArray = (Object[])args; if (args != null && argsArray.length > 0 && (argsArray[0] instanceof Closure)) { if (current == Environment.CUSTOM && current.getName().equals(name)) { callable = (Closure<?>) argsArray[0]; } return null; } throw new MissingMethodException(name, Environment.class, argsArray); } } private static boolean isBlank(String value) { return value == null || value.trim().length() == 0; } /** * @return the name of the environment */ public String getName() { return name; } /** * Set the name. * @param name the name */ public void setName(String name) { this.name = name; } /** * @return Returns whether reload is enabled for the environment */ public boolean isReloadEnabled() { final boolean reloadOverride = Boolean.getBoolean(RELOAD_ENABLED); getReloadLocation(); final boolean reloadLocationSpecified = hasLocation(reloadLocation); return this == DEVELOPMENT && reloadLocationSpecified || reloadOverride && reloadLocationSpecified; } /** * * @return Whether interactive mode is enabled */ public static boolean isInteractiveMode() { return Boolean.getBoolean(INTERACTIVE_MODE_ENABLED); } /** * * @return Whether interactive mode is enabled */ public static boolean isInitializing() { return initializingState; } public static void setInitializing(boolean initializing) { initializingState = initializing; System.setProperty(INITIALIZING, String.valueOf(initializing)); } /** * @return true if the reloading agent is active */ private static Boolean reloadingAgentEnabled = null; public static boolean isReloadingAgentEnabled() { if(reloadingAgentEnabled != null) { return reloadingAgentEnabled; } try { Class.forName("org.springsource.loaded.TypeRegistry"); reloadingAgentEnabled = Environment.getCurrent().isReloadEnabled(); } catch (ClassNotFoundException e) { reloadingAgentEnabled = false; } return reloadingAgentEnabled; } /** * @return Obtains the location to reload resources from */ public String getReloadLocation() { if(this.reloadLocation != null) { return this.reloadLocation; } String location = getReloadLocationInternal(); if (hasLocation(location)) { reloadLocation = location; return location; } return "."; // default to the current directory } private boolean hasLocation(String location) { return location != null && location.length() > 0; } /** * @return Whether a reload location is specified */ public boolean hasReloadLocation() { getReloadLocation(); return hasLocation(reloadLocation); } private String getReloadLocationInternal() { String location = System.getProperty(RELOAD_LOCATION); if(!hasLocation(location)) { location = System.getProperty(BuildSettings.APP_BASE_DIR); } if(!hasLocation(location)) { File current = new File(".", "grails-app"); if(current.exists()) { location = current.getParentFile().getAbsolutePath(); } else { current = new File(".", "settings.gradle"); if(current.exists()) { // multi-project build location = IOUtils.findApplicationDirectory(); } } } return location; } }