在 Grails Web 应用的第一个 运行 上复制默认外部配置

Copying default external configuration on first run of Grails web app

在我们的 Grails Web 应用程序中,我们希望使用外部配置文件,这样我们就可以在不发布新版本的情况下更改配置。我们还希望这些文件位于应用程序目录之外,以便它们在持续集成期间保持不变。

我们需要做的最后一件事是确保外部配置文件存在。如果他们不这样做,那么我们想创建它们,用预定义的内容(生产环境默认值)填充它们,然后像​​它们以前存在一样使用它们。这允许任何管理员更改应用程序的设置,而无需详细了解实际可用的选项。

为此,web-app/WEB-INF/conf 中有几个文件准备好在第一个 运行 应用程序时复制到外部配置位置。

到目前为止一切顺利。但我们需要在应用程序初始化之前执行此操作,以便将与生产相关的数据源定义修改考虑在内。

我可以在 Config.groovy 文件中进行复制和加载操作,但我目前不知道 WEB-INF/conf 目录的绝对位置。

如何在初始化的早期阶段获取位置?这个问题还有其他解决办法吗?

对此有一个最佳实践。

一般来说,永远不要写入部署应用程序的文件夹。你无法控制它。下一次推出将删除您在那里写的所有内容。

相反,利用真正专业人士使用的内置配置功能 (Spring and/or JPA)。

JNDI 是查找数据库、文件和 URL 等资源的规范。

运行时要配置JNDI,还请多多关照。

他们还需要一组初始配置文件,并随时准备根据开发团队的要求进行更改。

一如既往,所有配置文件都应该在您的源代码库中。

我终于设法通过使用 Java 定位放置在 class 路径上的资源的能力自己解决了这个问题。

我把后来复制到外面的.groovy文件放到了grails-app/conf目录下(在class路径上),然后在文件名后加了后缀,这样他们不会在打包应用程序时被编译。所以现在我有 *Config.groovy 个包含配置默认值(适用于所有环境)的文件和 *Config.groovy.production 个包含生产环境默认值的文件(覆盖预编译的默认值)。

现在 - Config.groovy 开始是这样的:

grails.config.defaults.locations = [ EmailConfig, AccessConfig, LogConfig, SecurityConfig ]

environments {
    production {
        grails.config.locations = ConfigUtils.getExternalConfigFiles(
            '.production',
            "${userHome}${File.separator}.config${File.separator}${appName}",
            'AccessConfig.groovy',
            'Config.groovy',
            'DataSource.groovy',
            'EmailConfig.groovy',
            'LogConfig.groovy',
            'SecurityConfig.groovy'
        )
    }
}

然后 ConfigUtils class:

public class ConfigUtils {

    // Log4j may not be initialized yet 
    private static final Logger LOG = Logger.getGlobal()

    public static def getExternalConfigFiles(final String defaultSuffix, final String externalConfigFilesLocation, final String... externalConfigFiles) {

        final def externalConfigFilesDir = new File(externalConfigFilesLocation)

        LOG.info "Loading configuration from ${externalConfigFilesDir}"

        if (!externalConfigFilesDir.exists()) {
            LOG.warning "${externalConfigFilesDir} not found. Creating..."
            try {
                externalConfigFilesDir.mkdirs()
            } catch (e) {
                LOG.severe "Failed to create external configuration storage. Default configuration will be used."
                e.printStackTrace()
                return []
            }
        }

        final def cl = ConfigUtils.class.getClassLoader()
        def result = []

        externalConfigFiles.each {

            final def file = new File(externalConfigFilesDir, it)
            if (file.exists()) {
                result << file.toURI().toURL()
                return
            }

            final def error = false
            final def defaultFileURL = cl.getResource(it + defaultSuffix)
            final def defaultFile

            if (defaultFileURL) {
                defaultFile = new File(defaultFileURL.toURI())
                error = !defaultFile.exists();
            } else {
                error = true
            }

            if (error) {
                LOG.severe "Neither of ${file} or ${defaultFile} exists. Skipping..."
                return
            }

            LOG.warning "${file} does not exist. Copying ${defaultFile} -> ${file}..."

            try {
                FileUtils.copyFile(defaultFile, file)
            } catch (e) {
                LOG.severe "Couldn't copy ${defaultFile} -> ${file}. Skipping..."
                e.printStackTrace()
                return
            }

            result << file.toURI().toURL()
        }

        return result
    }

}