Gradle issue using file and filter

I am trying to loop over a map, for each key read some property files, use filter to replace some tokens. My issue is, looping happens. At the end, instead of having 5 folders, I’ve just 1 folder inside config. Do I need to close any resources like file/inputstream before going on to the next?

task copyResByHost(type: Copy) {

java.util.HashMap hostMap = new HashMap();

hostMap.put(“devserver01”,“env_dev.properties”);
hostMap.put(“devserver02”,“env_dev.properties”);
hostMap.put(“devserver03”,“env_dev.properties”);
hostMap.put(“devserver04”,“env_dev.properties”);
hostMap.put(“devserver05”,“env_dev.properties”);

hostMap.each { key, value ->
from “$projectDir/resources/templates"
into(“build/config/${key}”)
def myProps = new Properties()
file(”$projectDir/resources/properties/${value}").withInputStream {
myProps.load(it);
}
file("$projectDir/resources/properties/${key}.properties").withInputStream {
myProps.load(it);
}
filter(org.apache.tools.ant.filters.ReplaceTokens, tokens: myProps)
}

}

Can you let me know where am I making a mistake which is causing 1 folder only created instead of 5 inside config? Thanks for your guidance.

You need to make sure that you are properly nesting your configuration in child copy specs. Your code, as shown, overwrites setting the destination directory and doesn’t constrain the ReplaceTokens values to only the intended values for each host.

If you’re going to use an ad-hoc task, I would rewrite it like this:

task copyResByHost(type: Copy) {

    ext.hostMap = [
        devserver01: 'env_dev.properties',
        devserver02: 'env_dev.properties',
        devserver03: 'env_dev.properties',
        devserver04: 'env_dev.properties',
        devserver05: 'env_dev.properties'
    ]

    into "${buildDir}/config"

    hostMap.each { key, value ->
        def myProps = new Properties()

        file("resources/properties/${value}").withInputStream {
            myProps.load(it);
        }

        file("resources/properties/${key}.properties").withInputStream {
            myProps.load(it);
        }

        into("${key}") {
            from 'resources/templates'
            filter(org.apache.tools.ant.filters.ReplaceTokens, tokens: myProps)
        }
    }

}

Thanks Justin. I changed my code to put a closure doLast { copy {} } like below. This helped resolve the issue.

    hostMap.each { key, value ->
    println "Creating configs for $key"
        doLast {
            copy {
                from("$projectDir/resources/templates")
                into("build/config/${key}")
                def myProps = new Properties()
                new File("$projectDir/resources/properties/${value}").withInputStream { stream ->
                    myProps.load(stream);
                }
                new File("$projectDir/resources/properties/${key}.properties").withInputStream { stream ->
                    myProps.load(stream);
                }
                filter(org.apache.tools.ant.filters.ReplaceTokens, tokens: myProps)
            }
        }
    }

If you add doLast { copy { ... } }, you are calling project.copy { ... }, not configuring the Copy task. You are losing all of the benefits of the Copy task, including UP-TO-DATE checking if you do this.

Thanks James. Let me try the way you have mentioned.

I am not sure if this below issue is also related to above issue in terms of maintaining proper scope. From below code, I’m trying to make uberJar of jars, binary files, configuration files, META-INF.

After the build is done, I only see uber jar containing lib & META-INF folders, but not config & bin folders.
config folder exists within build dir & bin folder is at rootDir.

tasks.withType(Jar) {
    destinationDir = file("$buildDir")
}

task copyBin(type: Copy) {
    into('${buildDir}/bin') {
        from('${rootDir}/bin') {
            include '**/**'
        }
    }
}

    task uberJar(type: Jar, dependsOn: [copyBin,copyResByHost]) {
    println "BuildDir is: ${rootDir}"

    into('bin') {
        from('${buildDir}/bin') {
            include '**/*'
        }
    }

    into('config') {
        from('${buildDir}/config') {
            include '**/**'
        }
    }

    into('lib') {
        from {
            configurations.runtime.collect {
                it.isDirectory() ? it : it.getAbsoluteFile()
            }
        }
    }
    into('META-INF') {
        from('./META-INF') {
            include '**/*'
        }
    }
}

Your from('${buildDir}/bin') and from('${buildDir}/config') are both single quoted, but using ${buildDir} requires string interpolation, so you must use double quotes. As written, it’s literally looking for a directory named ${buildDir}/bin or ${buildDir}/config, which doesn’t exist and therefore there’s no source to copy in to those destinations.

Thanks James. This helped.