在 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
}
}
在我们的 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
}
}