Copying default external configuration on first run of Grails web app - grails

In our Grails web applications, we'd like to use external configuration files so that we can change the configuration without releasing a new version. We'd also like these files to be outside of the application directory so that they stay unchanged during continuous integration.
The last thing we need to do is to make sure the external configuration files exist. If they don't, then we'd like to create them, fill them with predefined content (production environment defaults) and then use them as if they existed before. This allows any administrator to change settings of the application without detailed knowledge of the options actually available.
For this purpose, there's a couple of files within web-app/WEB-INF/conf ready to be copied to the external configuration location upon the first run of the application.
So far so good. But we need to do this before the application is initialized so that production-related modifications to data sources definitions are taken into account.
I can do the copy-and-load operation inside the Config.groovy file, but I don't know the absolute location of the WEB-INF/conf directory at the moment.
How can I get the location during this early phase of initialization? Is there any other solution to the problem?

There is a best practice for this.
In general, never write to the folder where the application is deployed. You have no control over it. The next rollout will remove everything you wrote there.
Instead, leverage the builtin configuration capabilities the real pro's use (Spring and/or JPA).
JNDI is the norm for looking up resources like databases, files and URL's.
Operations will have to configure JNDI, but they appreciate the attention.
They also need an initial set of configuration files, and be prepared to make changes at times as required by the development team.
As always, all configuration files should be in your source code repo.

I finally managed to solve this myself by using the Java's ability to locate resources placed on the classpath.
I took the .groovy files later to be copied outside, placed them into the grails-app/conf directory (which is on the classpath) and appended a suffix to their name so that they wouldn't get compiled upon packaging the application. So now I have *Config.groovy files containing configuration defaults (for all environments) and *Config.groovy.production files containing defaults for production environment (overriding the precompiled defaults).
Now - Config.groovy starts like this:
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'
)
}
}
Then the 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
}
}

Related

Groovy - How to reflect code from another groovy app to find Classes/Properties/Types within

First, I came from a .NET background so please excuse my lack of groovy lingo. Back when I was in a .NET shop, we were using TypeScript with C# to build web apps. In our controllers, we would always receive/respond with DTOs (data xfer objects). This got to be quite the headache every time you create/modify a DTO you had to update the TypeScript interface (the d.ts file) that corresponded to it.
So we created a little app (a simple exe) that loaded the dll from the webapp into it, then reflected over it to find the DTOs (filtering by specific namespaces), and parse through them to find each class name within, their properties, and their properties' data types, generate that information into a string, and finally saved as into a d.ts file.
This app was then configured to run on every build of the website. That way, when you go to run/debug/build the website, it would update your d.ts files automatically - which made working with TypeScript that much easier.
Long story short, how could I achieve this with a Grails Website if I were to write a simple groovy app to generate the d.ts that I want?
-- OR --
How do I get the IDE (ex IntelliJ) to run a groovy file (that is part of the app) that does this generation post-build?
I did find this but still need a way to run on compile:
Groovy property iteration
class Foo {
def feck = "fe"
def arse = "ar"
def drink = "dr"
}
class Foo2 {
def feck = "fe2"
def arse = "ar2"
def drink = "dr2"
}
def f = new Foo()
def f2 = new Foo2()
f2.properties.each { prop, val ->
if(prop in ["metaClass","class"]) return
if(f.hasProperty(prop)) f[prop] = val
}
assert f.feck == "fe2"
assert f.arse == "ar2"
assert f.drink == "dr2"
I've been able to extract the Domain Objects and their persistent fields via the following Gant script:
In scripts/Props.groovy:
import static groovy.json.JsonOutput.*
includeTargets << grailsScript("_GrailsBootstrap")
target(props: "Lists persistent properties for each domain class") {
depends(loadApp)
def propMap = [:].withDefault { [] }
grailsApp.domainClasses.each {
it?.persistentProperties?.each { prop ->
if (prop.hasProperty('name') && prop.name) {
propMap[it.clazz.name] << ["${prop.name}": "${prop.getType()?.name}"]
}
}
}
// do any necessary file I/O here (just printing it now as an example)
println prettyPrint(toJson(propMap))
}
setDefaultTarget(props)
This can be run via the command line like so:
grails props
Which produces output like the following:
{
"com.mycompany.User": [
{ "type": "java.lang.String" },
{ "username": "java.lang.String" },
{ "password": "java.lang.String" }
],
"com.mycompany.Person": [
{ "name": "java.lang.String" },
{ "alive": "java.lang.Boolean" }
]
}
A couple of drawbacks to this approach is that we don't get any transient properties and I'm not exactly sure how to hook this into the _Events.groovy eventCompileEnd event.
Thanks Kevin! Just wanted to mention, in order to get this to run, here are a few steps I had to make sure to do in my case that I thought I would share:
-> Open up the grails BuildConfig.groovy
-> Change tomcat from build to compile like this:
plugins {
compile ":tomcat:[version]"
}
-> Drop your Props.groovy into the scripts folder on the root (noting the path to the grails-app folder for reference)
[application root]/scripts/Props.groovy
[application root]/grails-app
-> Open Terminal
gvm use grails [version]
grails compile
grails Props
Note: I was using Grails 2.3.11 for the project I was running this on.
That gets everything in your script to run successfully for me. Now to modify the println portion to generate Typescript interfaces.
Will post a github link when it is ready so be sure to check back.

Reusing Grails variables inside Config.groovy

In my Config.groovy I have:
// Lots of other stuff up here...
environments {
development {
myapp.port = 7500
}
production {
myapp.port = 7600
}
}
fizz {
buzz {
foo = "Port #${myapp.port}"
}
}
When I run my app via grails -Dgrails.env=development run-app, my web app spins up without errors, but then at runtime I see that the value of fizz.buzz.foo is "Port #[:]". I would expect it to be "Port #7500".
Why isn't Grails seeing my var?
You could probably get away with this if myapp.port were not in an environments block but that's a side effect of the way Config.groovy is processed rather than being intentional. And if you were to override myapp.port in an external config file then fizz.buzz.foo would still end up with the value from Config.groovy, not the override from the external.
You could make it a late-binding GString using a closure to pull the value from grails.util.Holders.config when fizz.buzz.foo is referenced rather than when it is defined:
foo = "Port #${-> Holders.config.myapp.port}"
This is different from "Port #${Holders.config.myapp.port}" which would attempt to access the config at the point where Config.groovy is being parsed.
If the value you're defining here is one that will ultimately end up defining a property of a Spring bean (for example many of the spring-security-core plugin configuration options become bean properties) then you may be able to do
foo = 'Port #${myapp.port}'
with single rather than double quotes. This causes the resulting config entry to contain the literal string ${myapp.port}, which will be resolved against the config by the Spring property placeholder mechanism when it is used as a bean property value.
Another way is to simply use variables within your config file like this:
def appPort = 7500
environments {
production {
appPort = 7600
myapp.port = appPort
}
}
fizz {
buzz {
foo = "Port #$appPort"
}
}
And also, you don't need to send the -Dgrails.environment=development when you execute the run-app, it's the default one.

Externalized grails.serverURL not accessible from Config.groovy

I have an application where the config is externalized. In Config.groovy, I'm updating
grails.config.locations=[file:/.../myapp-log4j.groovy, file:/.../myapp-config.properties]
That works fine for datasources and such. But later in Config.groovy, I have:
springws {
wsdl {
MyApp {
// In this case the wsdl will be available at <grails.serverURL>/services/v1/myapp/myapp-v1.wsdl
wsdlName= 'myapp-v1'
xsds= '/WEB-INF/myapp.xsd'
portTypeName = 'myappPort'
serviceName = 'myappService'
locationUri = "${grails.serverURL}/services/v1/myapp"
targetNamespace = 'http://www..../myapp/v1/definitions'
}
}
}
And ${grails.serverURL} contains [:] which is not what is in my config file. The config file contains (among the datasource details):
grails.serverURL=http://samiel:9011/xid
My guess would be that the updated grails.config.locations is only used after I return from Config.groovy.
So, what are my options to setup my web service details based on the externalized serverURL ?
This is what I get when I run your example (just confirming your starting postion):
def testExternalConfig() {
println "grails.serverURL: ${ConfigurationHolder.config.grails.serverURL}"
println "springws.wsdl.MyApp.locationUri ${ConfigurationHolder.config.springws.wsdl.MyApp.locationUri}"
}
--Output from testExternalConfig--
grails.serverURL: http://samiel:9011/xid
springws.wsdl.MyApp.locationUri http://localhost:8080/soGrails/services/v1/myapp
Like you said, Config.groovy does not see the value set in the external config. I believe that Grails processes external
configs after Config.groovy, and this test appears to confirm that. The logic being that you likely have external config file
values that you want to have precedence over config in war file.
Fix is to override the full property in myapp-config.properties:
grails.serverURL=http://samiel:9011/xid
springws.wsdl.MyApp.locationUri=http://samiel:9011/xid/services/v1/myapp
With that change I get this:
--Output from testExternalConfig--
grails.serverURL: http://samiel:9011/xid
springws.wsdl.MyApp.locationUri http://samiel:9011/xid/services/v1/myapp

window/Linux Specific filesystem properties in Grails

I want to add linux based or windows based system properties in Grails as my app needs to run in the both. I know that we can add grails.config.locations location specified in Config. groovy.
But I need the if and esle condition for the file to be picked.
the problem is config.grrovy has userHome grailsHome appName appVersion
I would need something like osName.
Either I can go ahead with syetm.properties or if soembody can tell me how these (only) properties are availble in Config.groovy (through DefaultGrailsApplication or otherwise. that woyuld be great.
Also, somewjhat moer elegant would be if where I need those properties I make my service as user-defined-spring-bean. Would that be right and feasible approach?If yes, some example
You could do something like this in your Config.groovy:
environments {
development {
if (System.properties["os.name"] == "Linux") {
grails.config.locations = [ "file:$basedir/grails-app/conf/linux.properties" ]
} else {
grails.config.locations = [ "file:$basedir/grails-app/conf/windows.properties" ]
}
}
...
}
Alternatively, for a service based approach, you could bundle up all the OS specific behavior into implementations of service interface. For example:
// OsPrinterService.groovy
interface OsPrinterService {
void printOs();
}
// LinuxOsPrinterService.groovy
class LinuxOsPrinterService implements OsPrinterService {
void printOs() { println "Linux" }
}
// WindowsOsPrinterService.groovy
class WindowsOsPrinterService implements OsPrinterService {
void printOs() { println "Windows" }
}
Then instantiate the correct one in grails-app/conf/spring/resources.groovy like so:
beans = {
if (System.properties["os.name"] == "Linux") {
osPrinterService(LinuxOsPrinterService) {}
} else {
osPrinterService(WindowsOsPrinterService) {}
}
}
Then the correct service will be automatically injected into your objects by spring.
Create a custom enviorenment for both Windows and Linux. Something like the following should work if placed in config.groovy
environments {
productionWindows {
filePath=c:\path
}
productionLinux {
filePath=/var/dir
}
}
You should then be able to use the grails config object to get the value of filePath reguardless of weather your on Windows or Linux. For more details on this see section 3.2 of
http://www.grails.org/doc/1.0.x/guide/3.%20Configuration.html If you wanted to create a war file to run on Linux you would execute the following command.
grails -Dgrails.env=productionLinux war
And then to get a file path you stored in config.groovy for the specific environment your running in.
def fileToOpen=Conf.config.filePath
fileToOpen will contain the value you assigned to filePath in your config.groovy based on the environment your currently running as, so when running with productionLinux as the environment it will contain the value /var/dir

Configuration of Grails plugin

I'm developing my first Grails plugin. It has to access a webservice. The Plugin will obviously need the webservice url. What is the best way to configure this without hardcoding it into the Groovy classes? It would be nice with different config for different environments.
You might want to Keep It Simple(tm). You may define the URL directly in Config.groovy -including per-environment settings- and access it from your plugin as needed using grailsApplication.config (in most cases) or a ConfigurationHolder.config object (See further details in the manual).
As an added bonus that setting may also be defined in standard Java property files or on other configuration files specified in grails.config.locations.
e.g. in Config.groovy
// This will be the default value...
myPlugin.url=http://somewhe.re/test/endpoint
environments {
production {
// ...except when running in production mode
myPlugin.url=http://somewhe.re/for-real/endpoint
}
}
later, in a service provided by your plugin
import org.codehaus.groovy.grails.commons.ConfigurationHolder
class MyPluginService {
def url = ConfigurationHolder.config.myPlugin.url
// ...
}
If its only a small (read: one item) config option, it might just be easier to slurp in a properties file. If there are some number of configuration options, and some of them should be dynamic, i would suggest doing what the Acegi Security plugin does - add a file to /grails-app/conf/plugin_name_config.groovy perhaps.
added bonus is that the user can execute groovy code to compute their configuration options (much better over using properties files), as well as being able to do different environments with ease.
check out http://groovy.codehaus.org/ConfigSlurper , which is what grails internally use to slurp configs like config.groovy.
//e.g. in /grails-app/conf/MyWebServicePluginConfig.groovy
somePluginName {
production {
property1 = "some string"
}
test {
property1 = "another"
}
}
//in your myWebServicePlugin.groovy file, perhaps in the doWithSpring closure
GroovyClassLoader classLoader = new GroovyClassLoader(getClass().getClassLoader())
ConfigObject config
try {
config = new ConfigSlurper().parse(classLoader.loadClass('MyWebServicePluginConfig'))
} catch (Exception e) {/*??handle or what? use default here?*/}
assert config.test.property1.equals("another") == true

Resources