Create Development and User Specific Properties with Gradle - kercheval/GradleCMPlugin GitHub Wiki

The creation and use of properties files which are development environment specific is a common and often tedious task. This example shows one way that you can solve this problem using gradle.

Assuming you wanted to store property files in the dev/config directory. The following directory structure would contain property files that are additive.

  • prod - contains default properties that ops/deployment may use as boilerplate. These configuration files are used for production runs (not test)
  • dev - contains files named as in the prod directory, but contains only properties that are overriden form the base property file for dev runs.
  • user - contains user specific properties that override the properties found in dev. This directory would be in .gitignore to avoid checkin of user specific files.

Property loads occur in these directories in the following manner:

For each file in dev (ie foo_test.properties)

  • Compute the basefile name (ie foo.properties)
  • Load defaults from prod (if the base file exists)
  • Load defaults from dev (if the base file exists)
  • Load the properties from the original file
  • Load the overrides from user if an original file name exists
  • Save the properties in the ouput config directory (build/config)

Create the Configuration Properties

The following gradle logic correctly parses the config files

//
// define the configuration source and destination locations
//
def configDirName = 'dev/config'
def configDir = new File(projectDir, configDirName)

//
// define the prod/dev/user subdirectory names
//
def prodConfigDir = new File(configDir, 'prod')
def devConfigDir = new File(configDir, 'dev')
def userConfigDir = new File(configDir, 'user')

//
// make the config output directory location available to all projects
//
allprojects {
    ext {
        configOutputDir = new File(rootProject.buildDir, configDirName)
    }
}

//
// Create properties files for system usage
//
task configProperties {
    outputs.dir configOutputDir
    inputs.dir configDir

    doLast {
        configOutputDir.mkdirs()

        //
        // For every file in the dev dir, we need to obtain the defaults (from prod and dev 
        // using the base file name, the local overrides (from the file itself) and the user
        // overrides (from the user dir).  Base filenames are up to the leading '_'.  The 
        // custom name is from the leading '_' to the first '.'.
        //
        fileTree(devConfigDir).each { File file ->
            def baseFileName = file.name
            def customIndex = file.name.indexOf('_')
            if (customIndex != -1) {
                baseFileName = file.name.substring(0, customIndex) + file.name.substring(file.name.indexOf('.', customIndex + 1))
            }

            println('   Creating config file ' + file.name)
            def props = new SortedProperties()
            def prodFile = new File(prodConfigDir, baseFileName)
            def devFile = new File(devConfigDir, baseFileName)
            def userFile = new File(userConfigDir, file.name)

            if (prodFile.exists()) {
                props.load(new FileInputStream(prodFile))
            }
            if (devFile.exists()) {
                props.load(new FileInputStream(devFile))
            }
            props.load(new FileInputStream(file))
            if (userFile.exists()) {
                props.load(new FileInputStream(userFile))
            }
            props.store(new BufferedWriter(new FileWriter(configOutputDir.toString() + '/' + file.name)), null)
        }
    }
}


//
// A very simple class for sorting of standard properties
//
public class SortedProperties extends Properties {
    public synchronized Enumeration<Object> keys() {
        return Collections.enumeration(keySet())
    }

    public Set<Object> keySet() {
        return Collections.unmodifiableSet(new TreeSet<Object>(super.keySet()))
    }
}

Add Config files to Classpath

Once the user dependent configuration files have been created, they should be made available to the code base. The simplest and easiest way to do this is to load the properties from the class path

properties = getPropertiesFromClasspath(configFileName);

To find the files on the classpath, add the configuration files location to the classpath in your project like below

//
// Update the runtime classpath to include configuration files
//
sourceSets {
    main {
        //
        // Add the config directory to the runtime classpath
        //
        runtimeClasspath += files(rootProject.configOutputDir)
    }
}
⚠️ **GitHub.com Fallback** ⚠️