On the my project at work, there are configuration files that are used to configure the artifact for the various environments it gets deployed to. We’ve got about 6 different environments some with remarkably similar configurations, and that means lots of duplicate property values. With the current setup using maven resource filtering, if one environment is different from the other 5, the property has to be specified in all of the filtering config files. Worst case scenario, that means 5 files have duplicate data, and the 6th has something different.
Well, that was driving me crazy and so I implemented a groovy based solution using a YAML file instead.
I replaced all the maven resource filtering plugin stuff (with a few exceptions) with an execution from the GMaven Plugin. We’re already using the GMaven plugin for compiling our groovy test code, and a few groovy main classes.
The following groovy source file is generic enough to be used anywhere, with a small amount of tweaking. It’s got some project specific logic for our project, regarding a directory for output of the configured files. I think we’re putting out location specific files, even though they’re all for one location. So that’s just been hard-coded into this file. It will likely evolve if/when we add another location specific config. There’s a small bit of voodoo magic in there to use a SimpleTemplateEngine to perform simple filtering on the yaml file itself allowing you to do things like: ${project.basedir}, which can be quite handy when referencing simple databases for testing.
importjava.util.regex.Patternimportorg.yaml.snakeyaml.Yamlimportgroovy.text.SimpleTemplateEngineclassNoSuchResourceExceptionextendsException{NoSuchResourceException(Stringreason){super(reason)}}classGroovyMavenFilter{defdatafinaldefPATTERN=Pattern.compile(/.*(\$\{(.*)\}).*/)GroovyMavenFilter(StringconfigYaml,defvariables){Yamlyaml=newYaml();Filef=newFile(configYaml)defengine=newSimpleTemplateEngine()//This gives it access to the simple maven properties that exist// project.basedir, etc.data=yaml.load(engine.createTemplate(f.getText()).make(variables).toString())}defenvironments(){data.keySet().collect{if(it!="common")it}-null}/** * This actually filters the file * Follows the maven resource convention of ${thingy.thinger}* @param sourceFile * @param targetEnv * @param destinationFile * @return */deffilterFile(defsourceFile,deftargetEnv,defdestinationFile){Filesource=newFile(sourceFile)Filedestination=newFile(destinationFile)//Make sure we've got a directory to barf the files intodestination.getParentFile().mkdirs()destination.withPrintWriter{writer->source.eachLine{line->defmatcher=PATTERN.matcher(line)if(matcher.matches()){//println "matched ${matcher.groupCount()} groups"//println("matched group: ${matcher.group(1)}")//println "group 2: ${matcher.group(2)}"defg2=matcher.group(2)defg1=matcher.group(1)Stringreplacement=nullif(data[targetEnv].containsKey(g2)){replacement=data[targetEnv][g2]}elseif(data["common"].containsKey(g2)){replacement=data["common"][g2]}else{thrownewNoSuchResourceException("No such resource defined: ${g2}")}if(replacement==null){replacement=""}writer.println(line.replace(g1,replacement))}else{writer.println(line)}}}}}deffs=File.separatordefsourcePath="${project.basedir}${fs}src${fs}main${fs}config"deffilterConfig="${project.basedir}${fs}src${fs}main${fs}filters${fs}filter.yaml"//Pass in the simple variables that make it all workGroovyMavenFiltergmf=newGroovyMavenFilter(filterConfig,this.binding.variables)Fileconfigs=newFile(sourcePath)gmf.environments().each{env->configs.eachFile{file->//target/properties/test/ordprintln"Filtering file: ${file.getAbsolutePath()} in environment ${env} to some path"gmf.filterFile(file.getAbsolutePath(),env,"${project.basedir}${fs}target${fs}properties${fs}${env}${fs}ord${fs}${file.getName()}")}}
The settings in the more specific environment would override the common settings. So for the “dev” environment, my.authority would be set to www.example.com not localhost:8185, but it would be localhost:8185 for the local environment.
If you specified a property that didn’t exist, the groovy script would through a nice exception for you, so that you know you’re referencing a property that doesn’t exist.