All Downloads are FREE. Search and download functionalities are using the official Maven repository.

grails.util.Environment.groovy Maven / Gradle / Ivy

/*
 * 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.transform.CompileStatic
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 org.slf4j.Logger
import org.slf4j.LoggerFactory

import java.lang.management.ManagementFactory
import java.nio.file.Files
import java.util.function.Supplier
import java.util.jar.Attributes
import java.util.jar.Manifest

/**
 * Represents the current environment.
 *
 * @author Graeme Rocher
 * @since 1.1
 */
@CompileStatic
enum Environment {

    /** The development environment */
    DEVELOPMENT,

    /** The production environment */
    PRODUCTION,

    /** The test environment */
    TEST,

    /**
     * For the application data source, primarily for backward compatibility for those applications
     * that use ApplicationDataSource.groovy.
     */
    APPLICATION,

    /** A custom environment */
    CUSTOM

    private static final Supplier LOG = SupplierUtil.memoized(() -> LoggerFactory.getLogger(Environment.class))

    /**
     * Constant used to resolve the environment via System.getProperty(Environment.KEY)
     */
    public static String KEY = "grails.env"

    /**
     * Constant used to resolve the environment via System.getenv(Environment.ENV_KEY).
     */
    public static final String ENV_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 envNameMappings = CollectionUtils. 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 cachedCurrentEnvironment = new Holder<>("Environment")
    private static final boolean DEVELOPMENT_MODE = getCurrent() == DEVELOPMENT && BuildSettings.GRAILS_APP_DIR_PRESENT
    private static Boolean RELOADING_AGENT_ENABLED = null
    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
     */
    static String getGrailsVersion() {
        return GRAILS_VERSION
    }

    static void setCurrentReloadError(Throwable currentReloadError) {
        Environment.@currentReloadError = currentReloadError
    }

    static MultipleCompilationErrorsException getCurrentCompilationError() {
        return currentCompilationError
    }

    static Throwable getCurrentReloadError() {
        return currentReloadError
    }

    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.
     */
    static Environment getCurrent() {
        String envName = getEnvironmentInternal()

        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 = getEnvironmentInternal()

        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
     */
    static Environment getCurrentEnvironment() {
        return getCurrent()
    }

    /**
     * Reset the current environment
     */
    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
     */

    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
     */
    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
     */
    static boolean isDevelopmentRun() {
        Environment env = Environment.getCurrent()
        return isDevelopmentEnvironmentAvailable() && Boolean.getBoolean(RUN_ACTIVE) && (env == Environment.DEVELOPMENT)
    }

    /**
     * Checks if the run of the app is due to spring dev-tools or not.
     * @return True if spring-dev-tools restart
     */
    static boolean isDevtoolsRestart() {
        File pidFile = new File(BuildSettings.TARGET_DIR.toString() + File.separator + ".grailspid")
        LOG.get().debug("Looking for pid file at: {}", pidFile)
        boolean isDevToolsRestart = false
        try {
            if (Environment.isDevelopmentMode()) {
                String pid = ManagementFactory.getRuntimeMXBean().getName()
                if (pidFile.exists()) {
                    if (pid.equals(Files.readAllLines(pidFile.toPath()).get(0))) {
                        LOG.get().debug("spring-dev-tools restart detected.")
                        isDevToolsRestart = true
                    } else {
                        LOG.get().debug("spring-dev-tools first app start - creating pid file.")
                        writeDevToolsPidFile(pidFile, pid)
                    }
                } else {
                    LOG.get().debug("spring-dev-tools pid file did not exist.")
                    writeDevToolsPidFile(pidFile, pid)
                }
            }
        } catch (Exception ex) {
            LOG.get().error("spring-dev-tools restart detection error: {}", ex)
        }
        LOG.get().debug("spring-dev-tools restart: {}", isDevToolsRestart)
        return isDevToolsRestart
    }

    private static void writeDevToolsPidFile(File pidFile, String content) {
        BufferedWriter writer = null
        try {
            writer = new BufferedWriter(new FileWriter(pidFile))
            writer.write(content)
        } catch (Exception ex) {
            LOG.get().error("spring-dev-tools restart unable to write pid file: {}", ex)
        } finally {
            try {
                if (writer != null) {
                    writer.flush()
                    writer.close()
                }
            } catch (Exception ignored) {
            }
        }
    }

    /**
     * Check whether the application is deployed
     * @return true if is
     */
    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
     */
    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
     */
    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
     */
    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
     */
    static boolean isWithinShell() {
        return DefaultGroovyMethods.getRootLoader(Environment.class.getClassLoader()) != null
    }

    /**
     * @return Return true if the environment has been set as a System property
     */
    static boolean isSystemSet() {
        return getEnvironmentInternal() != null
    }

    /**
     * Returns the environment for the given short name
     * @param shortName The short name
     * @return The Environment or null if not known
     */
    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:
     *
     * 
     * environments {
     *      development {}
     *      production {}
     * }
     * 
     *
     * 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
     */
    static Closure getEnvironmentSpecificBlock(Closure closure) {
        final Environment env = getCurrent()
        return getEnvironmentSpecificBlock(env, closure)
    }

    /**
     * Takes an environment specific DSL block like:
     *
     * 
     * environments {
     *      development {}
     *      production {}
     * }
     * 
     *
     * 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
     */
    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:
     *
     * 
     * environments {
     *      development {}
     *      production {}
     * }
     * 
     *
     * And executes the closure that relates to the current environment
     *
     * @param closure The top level closure
     * @return The result of the closure execution
     */
    static Object executeForCurrentEnvironment(Closure closure) {
        final Environment env = getCurrent()
        return executeForEnvironment(env, closure)
    }

    /**
     * Takes an environment specific DSL block like:
     *
     * 
     * environments {
     *      development {}
     *      production {}
     * }
     * 
     *
     * 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
     */
    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 boolean isBlank(String value) {
        return value == null || value.trim().length() == 0
    }

    /**
     * @return the name of the environment
     */
    String getName() {
        return name
    }

    /**
     * Set the name.
     * @param name the name
     */
    void setName(String name) {
        this.name = name
    }

    /**
     * @return Returns whether reload is enabled for the environment
     */
    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
     */
    static boolean isInteractiveMode() {
        return Boolean.getBoolean(INTERACTIVE_MODE_ENABLED)
    }

    /**
     *
     * @return Whether interactive mode is enabled
     */
    static boolean isInitializing() {
        return initializingState
    }

    static void setInitializing(boolean initializing) {
        initializingState = initializing
        System.setProperty(INITIALIZING, String.valueOf(initializing))
    }

    /**
     * @return true if the reloading agent is active
     */
    static boolean isReloadingAgentEnabled() {
        if (RELOADING_AGENT_ENABLED != null) {
            return RELOADING_AGENT_ENABLED
        }
        try {
            Class.forName("org.springframework.boot.devtools.RemoteSpringApplication")
            RELOADING_AGENT_ENABLED = Environment.getCurrent().isReloadEnabled()
            LOG.get().debug("Found spring-dev-tools on the class path")
        }
        catch (ClassNotFoundException e) {
            RELOADING_AGENT_ENABLED = false
            try {
                Class.forName("org.springsource.loaded.TypeRegistry")
                String jvmVersion = System.getProperty("java.specification.version")
                LOG.get().debug("Found spring-loaded on the class path")
                RELOADING_AGENT_ENABLED = Environment.getCurrent().isReloadEnabled()
            }
            catch (ClassNotFoundException e1) {
                RELOADING_AGENT_ENABLED = false
            }
        }
        return RELOADING_AGENT_ENABLED
    }

    /**
     * @return Obtains the location to reload resources from
     */
    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
     */
    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
    }

    private static String getEnvironmentInternal() {
        String envName = System.getProperty(Environment.KEY)
        return isBlank(envName) ? System.getenv(Environment.ENV_KEY) : envName
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy