How Do I Combine Property Files In Gradle? - gradle

I have an ant task that combines 2 property files together to make a single properties file.
The base.config contains the majority of the properties and the override.config contains one or more properties that modifies the value in the base.config
<!--Combine the base.config and override.config together to create a properties file-->
<target name="generateProperties" depends="init">
<property prefix="app.properties" file="override.config"/>
<property prefix="app.properties" file="base.config"/>
<echoproperties destfile="${build.dir}/combined.properties">
<propertyset>
<propertyref prefix="app.properties"/>
<mapper type="glob" from="app.properties.*" to="*"/>
</propertyset>
</echoproperties>
</target>
How do I do the same thing in gradle (without importing the ant task)?

There is a WriteProperties task that can reliably save the properties.
You then just have to pass the Properties in a way that the base is overridden.
e.g.
task generateProperties(type: WriteProperties) {
Properties combinedProperties = new Properties()
File baseProperties = file('base.config')
baseProperties.withInputStream {
combinedProperties.load(it)
}
File overrideProperties = file('override.config')
overrideProperties.withInputStream {
combinedProperties.load(it)
}
properties = combinedProperties
outputFile = file("$buildDir/combined.properties")
}

There are (5) property 'scopes' Gradle has for finding properties. They are:
Project instance itself
Extra properties
Extensions
Conventions
Tasks
Project parent (recursive lookup with the above)
You can read a more in-depth overview here: https://docs.gradle.org/current/javadoc/org/gradle/api/Project.html#properties
Gradle searches for properties in that order. So you can define base properties lower in the chain and provide overrides higher up in the chain.

Related

Setting gradle system property in .gradle file

I have a "general.gradle" file that sets the common properties for all of my projects.
This file is committed to git repository and shared among many users.
I would like to add a system property to is so it will be common to all the users
such options like systemProp.http.nonProxyHosts
is there a way?
You could make another file, like general.properties, add your system properties there prefixed by systemProp and then in general.gradle load the properties from that file, like so:
FileInputStream fileInputStream = new FileInputStream(new File('{YOUR_PATH}/general.properties'))
Properties properties = new Properties()
properties.load(fileInputStream)
fileInputStream.close()
properties.stringPropertyNames().forEach({key -> ext.set(key, properties.getProperty(key))})
and then load it to your root build.gradle file in projects, like so:
apply from: '{YOUR_PATH}/general.gradle'
You can retrieve it from the ext property. Following this example, if you put general.properties in your project and add there,for example: spring=dev. Then you put the property loading code in general.gradle and apply it in your build.gradle, if you add a task like this in your build.gradle:
task testProp << {
String profile = getProperty('spring')
System.setProperty('Spring.profiles.active', profile)
String prop = System.getProperty('Spring.profiles.active');
println prop
}
then the task execution should print out dev.

How to use ant.properties in a `vendor.gradle` vendor settings file?

I have a multi-project gradle build with an ant build script that contains a task such as this (simplified):
<target name="get-version">
<!-- Goes out and fetches the versioning for each project and sets prop -->
<property name="version" value="${myDesiredVersion}" />
</target>
and inside my build.gradle script I have an ant script loader and then I depend on this target; my issue is that this isn't being evaluated in a way that allows me to use my ant.properties['version'] in vendor.gradle like
// version = '1.0.2' -- works
version = ant.properties['version'] // doesn't work
I'm a gradle noobie and to top it off I'm migrating an ant project to start using gradle so I could be way off course. It's important to note that I can't just outright include the version in the vendor.gradle file since it's being generated by the ant-script.
I'm not familiar with how I could insert this ahead of the gradle lifecycle... I tried something like this
gradle.beforeProject { p ->
tasks['get-version'].execute() // appears to execute successfully?
configure(p) {
def vendorSettings = file("${myRoot}/vendor.gradle")
if(vendorSettings.exists()){
println "Loading vendor settings for project " + p
println ant.properties['version'] // but outputs null here
// ant.properties is still a valid observable map
apply from: vendorSettings
}
}
}
and the ant version property was still null - note that it works outside of this, I think the scoping is a little different then I think it is in this specific situation
You might have something like
task antTask {
//...
doLast {
file("vendor.properties").withInputStream { is ->
Properties props = new Properties()
props.load(is)
props.each { prop -> ext.set(prop.key, prop.value) }
}
}
}
And after the task is executed you could access properties like ext.version, etc.
But, this won't work if you want to use the properties in task configurations, because all the tasks are configured first, only after that are executed if necessary. So your antTask is running only after all other tasks are already configured. So you probably need to rethink how you generate these ant properties and either precompute them before running gradle, or port the logic to gradle build.

How to handle multiple build targets, e.g. dev, test, main?

I'm currently migrating from Maven to SBT, and I'm struggling to understand how I can handle multiple build targets (dev, test, train, prod etc).
For example, I have a persistence.xml that looks like this:
<properties>
<property name="javax.persistence.jdbc.driver" value="oracle.jdbc.driver.OracleDriver"/>
<property name="javax.persistence.jdbc.url" value="${db.connectionURL}"/>
<property name="javax.persistence.jdbc.user" value="${db.username}"/>
<property name="javax.persistence.jdbc.password" value="${db.password}"/>
<property name="eclipselink.target-database" value="Oracle10"/>
</properties>
With Maven, it was really easy to handle this using profiles.
I've already tried what was suggested here for SBT, but I'm not having any success with that approach.
How to Add Environment Profile Config to SBT.
Also, using that approach I would need a new directory for each new environment. I just think that there has to be a better way to handle this kind of setup using SBT?
tl;dr Use ivyConfigurations to add custom configs and resourceGenerators to process files per environment.
All credits goes to Eugene Yokota for the answer to How to Add Environment Profile Config to SBT. There are some modification that make the solution of mine...cough...cough...slightly better.
The following build.sbt defines two new configurations - dev and qa. It also defines resourceGenerators per configuration that effectively gives a way to access what configuration the new resourceGenerator executes in:
val Dev = config("dev") extend Runtime
val Qa = config("qa") extend Runtime
ivyConfigurations ++= Seq(Dev, Qa)
// http://www.scala-sbt.org/0.13.5/docs/Howto/generatefiles.html#resources
lazy val bareResourceGenerators: Seq[Setting[_]] = Seq(
resourceGenerators += Def.task {
val file = resourceManaged.value / "demo" / "myapp.properties"
println(s"Inside ${configuration.value}")
val contents = s"config=${configuration.value}"
IO.write(file, contents)
Seq(file)
}.taskValue
)
inConfig(Dev)(Defaults.configSettings ++ bareResourceGenerators)
inConfig(Qa)(Defaults.configSettings ++ bareResourceGenerators)
Inside the new resourceGenerator you can do whatever you want and the per-configuration processing is possible with the configuration setting that gives you the name of the configuration:
> show dev:configuration
[info] dev
> show qa:configuration
[info] qa
Now when you execute show qa:resources you'll see that there are two files generated with target/scala-2.10/resource_managed/qa/demo/myapp.properties with the content that's specific to a configuration:
> show qa:resources
Inside qa
[info] List(/Users/jacek/sandbox/envs/target/scala-2.10/resource_managed/qa/demo/myapp.properties, /Users/jacek/sandbox/envs/src/qa/resources)
The trick now is to use the resourceGenerator to meet your needs and since you're in Scala code you can do whatever you want - just use configuration.value as the qualifier for a configuration-specific code.
Say, you want to use a qa-specific properties file in the standard src/main/resources directory. Just know where the value is bound to (what configuration and setting the value comes from). It's just compile:resourceDirectory.
> show compile:resourceDirectory
[info] /Users/jacek/sandbox/envs/src/main/resources
Just use resourceDirectory in Compile whenever you need a "stable" (aka configuration-fixed) value like src/main/resources.
val props = (resourceDirectory in Compile).value / s"${configuration.value.name}.properties"
println(s"Read files from $props")
With the above lines you'd get:
> show qa:resources
Inside qa
Read files from /Users/jacek/sandbox/envs/src/main/resources/qa.properties
[info] List(/Users/jacek/sandbox/envs/target/scala-2.10/resource_managed/qa/demo/myapp.properties, /Users/jacek/sandbox/envs/src/qa/resources)
> show dev:resources
Inside dev
Read files from /Users/jacek/sandbox/envs/src/main/resources/dev.properties
[info] List(/Users/jacek/sandbox/envs/target/scala-2.10/resource_managed/dev/demo/myapp.properties, /Users/jacek/sandbox/envs/src/dev/resources)

How to expand property references in jar resources?

I'm using Gradle to build a jar containing an xml file in META-INF. This file has a row like
<property name="databasePlatform" value="${sqlDialect}" />
to allow for different SQL databases for different environments. I want to tell gradle to expand ${sqlDialect} from the project properties.
I tried this:
jar {
expand project.properties
}
but it fails with a GroovyRuntimeException that seems to me like the Jar task attempts to expand properties in .class files as well. So then I tried
jar {
from(sourceSets.main.resources) {
expand project.properties
}
}
which does not throw the above exception, but instead results in all resources being copied twice - once with property expansion and once without. I managed to work around this with
jar {
eachFile {
if(it.relativePath.segments[0] in ['META-INF']) {
expand project.properties
}
}
}
which does what I want, since in my use case I only need to expand properties of files in the META-INF directory. But this feels like a pretty ugly hack, is there a better way to do this?
I stumbled across this post in a thread about a different but closely related issue. Turns out you want to configure the processResources task, not the jar task:
processResources {
expand project.properties
}
For some reason, though, I did have to clean once before Gradle noticed the change.
In addition to #emil-lundberg 's excellent solution, I'd limit the resource processing to just the desired target file:
build.gradle
processResources {
filesMatching("**/applicationContext.xml") {
expand(project: project)
}
}
An additional note: if the ${...} parentheses are causing "Could not resolve placeholder" errors, you can alternatively use <%=...%>. N.B. tested with a *.properties file, not sure how this would work for an XML file.
I've had similar problems migrating from maven to gradle build. And so far the simplest/easiest solution was to simply do the filtering yourself such as:
processResources {
def buildProps = new Properties()
buildProps.load(file('build.properties').newReader())
filter { String line ->
line.findAll(/\$\{([a-z,A-Z,0-9,\.]+)\}/).each {
def key = it.replace("\${", "").replace("}", "")
if (buildProps[key] != null)
{
line = line.replace(it, buildProps[key])
}
}
line
}
}
This will load all the properties from the specified properties file and filter all the "${some.property.here}" type placeholders. Fully supports dot-separated properties in the *.properties file.
As an added bonus, it doesn't clash with $someVar type placeholders like expand() does. Also, if the placeholder could not be matched with a property, it's left untouched, thus reducing the possibility of property clashes from different sources.
here is what worked for me (Gradle 4.0.1) in a multi-module project:
in /webshared/build.gradle:
import org.apache.tools.ant.filters.*
afterEvaluate {
configure(allProcessResourcesTasks()) {
filter(ReplaceTokens,
tokens: [myAppVersion: MY_APP_VERSION])
}
}
def allProcessResourcesTasks() {
sourceSets*.processResourcesTaskName.collect {
tasks[it]
}
}
and my MY_APP_VERSION variable is defined in top-level build.gradle file:
ext {
// application release version.
// it is used in the ZIP file name and is shown in "About" dialog.
MY_APP_VERSION = "1.0.0-SNAPSHOT"
}
and my resource file is in /webshared/src/main/resources/version.properties :
# Do NOT set application version here, set it in "build.gradle" file
# This file is transformed/populated during the Gradle build.
version=#myAppVersion#
I took your first attempt and created a test project. I put a pom file from a jenkins plugin in ./src/main/resources/META-INF/. I assume it is a good enough xml example. I replaced the artifactId line to look like the following:
<artifactId>${artifactId}</artifactId>
My build.gradle:
apply plugin: 'java'
jar {
expand project.properties
}
When I ran gradle jar for the first time it exploded because I forgot to define a value for the property. My second attempt succeeded with the following commandline:
gradle jar -PartifactId=WhoCares
For testing purposes I just defined the property using -P. I'm not sure how you are trying to define your property, but perhaps that is the missing piece. Without seeing the stacktrace of your exception it's hard to know for sure, but the above example worked perfectly for me and seems to solve your problem.

Overriding a few properties for junit test using spring or camel property placeholder in a maven layout

I want to specify only the properties I want to override in a test properties file of the same name in the src/test/resources folder.
A little more detail...
In a maven layout I have a properties file that contains the deployed value to use (eg. input.uri).
src/main/resources/app.properties:
input.uri=jms:topic:in
app.name=foo_bar
This file's properties are loaded into the context.xml file by the property-placeholder:
src/main/resources/META-INF/spring/context.xml:
<context:property-placeholder properties-ref="springProperties"/>
<util:properties id="springProperties" local-override="true" location="classpath:app.properties" />
I have the test properties file with the same name, app.properties, in src/test/resources folder and override the input.uri definition to one that my junit test will use. (note, app.name doesn't change).
src/test/resources/app.properties:
input.uri=seda:in
How would you write the junit test and/or a test context.xml file so that properties are loaded from the src/main/resources/app.properties file, but any properties defined in the src/test/resources/app.properties file override the ones in the src/main/resources/app.properties file? Without it being obvious that you're loading two different files either in the src/main files or src/test junit test files - I want the property placeholder to search the classpath and pick the right values.
You will have to provide a different name though - if both the properties in the main and the test have the same name, the entire properties in one or the other will take effect.
Instead an approach like this has worked for me:
In your src/main/resources/META-INF/spring/context.xml do this:
<context:property-placeholder location="classpath:app.properties" local-override="true" properties-ref="springProperties"/>
<util:properties id="springProperties">
</util:properties>
In a test-context.xml file:
<import resource="classpath:/META-INF/spring/context.xml">
<util:properties id="springProperties"> <!-- or refer to a overriding file -->
<prop key="input.uri">seda.in</prop>
</util:properties>
This will override the properties for you, while maintaining the not overridden values from the original file.

Resources