Automatic Property Expansion Using Gradle with escape characters - java

I'm using property expansion in Gradle. All works fine but for file-paths.
I am using the processResources task in Gradle
processResources {
filesMatching ('**/application.properties') {
expand project.properties
}
}
I have a property in my Spring application.properties as follows:
root.location = ${rootDir}
In my build.gradle I have defined the following:
ext['rootDir'] = rootProject.buildDir.path + File.separator + "tmp"
Result I get in application.properties is
root.location = D:\Projects\myproject\build\tmp
Which turns into D:Projectsmyprojectbuild\tmp in my Spring class when doing:
#Value("${cpm.repository.root.location}") String rootLocation;
I need the property to be expanded to:
root.location = D:\\projects\\myproject\\build\\tmp
Because that would result in D:\projects\myproject\build\tmp
What am I doing wrong? Expansion does work as intended, it's just that the '\' in the path are not escapped when expanded.
Thanks in advance

I think you should use processResources task to modify your resources.
We are using it that way (with application.yml and Gradle Kotlin DSL):
tasks {
named<ProcessResources>("processResources") {
outputs.upToDateWhen { false }
filesMatching("**/application.yml") {
filter {
it.replace("#project.version#", version as String)
}
filter {
it.replace("#spring.profiles.active#", profiles)
}
}
}
}
And of course #spring.profiles.active# is a string you want to replace in your file.

Related

Spring List all files in static directory

I use Spring with embeded Tomcat and a War file in prodution.
I need to list all files in the "static" directory. It's like 4 days I m on it
This kind of code not working :
Stream.of(new File(dir).listFiles()
It lists the files where the war is located but the static directory is inside the war
With this code I arrive to read a file inside the war :
Thread.currentThread().contextClassLoader?.getResourceAsStream(path)?.bufferedReader()?.use(BufferedReader::readText)
But it's not working with a directory
And now i have no idea how i can list all files inside the static directory
I found a solution
Not sure it's the best and clean but it's worked
I use 2 way to do it, one for developpment and the other when it's package inside war
loadFilesFromDirectory("/static")
fun listFilesFromDirectory(path: String, request: HttpServletRequest? = null): List<String> {
return if (request == null || request.requestURL.contains("localhost")) {
//Don't work on war but work on IDE
Thread.currentThread().contextClassLoader?.getResourceAsStream(path)
?.bufferedReader()?.use(BufferedReader::readText)?.split("\n")
?.filter { it.endsWith(".html") }
?: throw Exception("Non trouvé :$path")
} else {
//Not work on IDE but work on war
val root: URL = request.servletContext.getResource("/WEB-INF/classes/$path")
var rootPath: String = root.path
rootPath = rootPath.substring(rootPath.indexOf("/WEB-INF"), rootPath.length)
return request.servletContext.getResourcePaths(rootPath).map {
it.replace(".*/", "")
}.filter { it.endsWith(".html") }
}
}

Differentiate Dependency based on profile in Gradle file

I wanted to differentiate my dependencies based on different environments. I'm not getting correct env value along with correct conditions to categories dependency.
def profile = project.hasProperty("spring.profiles.active") ?
project.property("spring.profiles.active") :
System.getProperty("spring.profiles.active", 'local')
bootRun {
systemProperty "spring.profiles.active", profile
}
I expect the output something like below but profile variable is not getting correct profile value
dependencies{
if(profile == "dev"){
compile('com.oracle:ojdbc6:+')
}
if(profile == "prod"){
compile('commons-dbcp:commons-dbcp:1.4')
}}
You can proceed differently by having build_[profile].gradle for each profile, where [profile] is the profile you pass as argument when you launch your application example ( via -P ) :
./gradlew -Pprod bootRun
So suppose you have 2 environment prod and local, in your build.gradle you will have :
def currentProfile;
if (project.hasProperty('prod')) {
currentProfile = 'production';
apply from: rootProject.file('gradle/build_prod.gradle');
} else if (project.hasProperty('local')) {
currentProfile = 'local';
apply from: rootProject.file('gradle/build_local.gradle');
} else {
currentProfile = 'default profile';
apply from: rootProject.file('gradle/build_default.gradle');
}
println 'Current profile: "' + currentProfile + '"
You should also have 2 gradle files; build_prod.gradle and build_local.gradle, and there you could have different dependencies and configuration as you want.

Gradle extension properties in subproject

I have a Java project (builded with Gradle 4.6) which contains few modules and every module has it's own build.gradle. Also there is build.gradle in the main project. So the structure looks like:
/
/build.gradle
/module-number-one/build.gradle
/module-number-two/build.gradle
Everything works good, but I cannot access extension properties from main script. For example in /module-number-one/build.gradle I have:
ext {
myProp = 'some value'
}
And when I do:
allprojects.findAll { Project project ->
println(project.ext.has('myProp'))
// or
println(project.hasProperty('myProp'))
// or
println(project
.getExtensions()
.getExtraProperties()
.getProperties()
.contains('myProp'))
}
I receiving false. Is it possible to access ext only from the same file or I'm doing something wrong?
The problem with the project property evaluation, if you put the property reference after the project.afterEvaluate, it should work:
task findModule {
subprojects {
project -> project.afterEvaluate {
println(project.name + ": " + project.ext.has('cons'))
println(project.name + ": " + project.hasProperty('cons'))
}
}
}
output:
$ gradle -q findModule
module-number-one: true
module-number-one: true
module-number-two: false
module-number-two: false

How to read a properties files and use the values in project Gradle script?

I am working on a Gradle script where I need to read the local.properties file and use the values in the properties file in build.gradle. I am doing it in the below manner. I ran the below script and it is now throwing an error, but it is also not doing anything like creating, deleting, and copying the file. I tried to print the value of the variable and it is showing the correct value.
Can someone let me know if this is the correct way to do this? I think the other way is to define everything in the gradle.properties and use it in the build.gradle. Can someone let me know how could I access the properties in build.gradle from build.properties?
build.gradle file:
apply plugin: 'java'
// Set the group for publishing
group = 'com.true.test'
/**
* Initializing GAVC settings
*/
def buildProperties = new Properties()
file("version.properties").withInputStream {
stream -> buildProperties.load(stream)
}
// If jenkins build, add the jenkins build version to the version. Else add snapshot version to the version.
def env = System.getenv()
if (env["BUILD_NUMBER"]) buildProperties.test+= ".${env["BUILD_NUMBER"]}"
version = buildProperties.test
println "${version}"
// Name is set in the settings.gradle file
group = "com.true.test"
version = buildProperties.test
println "Building ${project.group}:${project.name}:${project.version}"
Properties properties = new Properties()
properties.load(project.file('build.properties').newDataInputStream())
def folderDir = properties.getProperty('build.dir')
def configDir = properties.getProperty('config.dir')
def baseDir = properties.getProperty('base.dir')
def logDir = properties.getProperty('log.dir')
def deployDir = properties.getProperty('deploy.dir')
def testsDir = properties.getProperty('tests.dir')
def packageDir = properties.getProperty('package.dir')
def wrapperDir = properties.getProperty('wrapper.dir')
sourceCompatibility = 1.7
compileJava.options.encoding = 'UTF-8'
repositories {
maven { url "http://arti.oven.c:9000/release" }
}
task swipe(type: Delete) {
println "Delete $projectDir/${folderDir}"
delete "$projectDir/$folderDir"
delete "$projectDir/$logDir"
delete "$projectDir/$deployDir"
delete "$projectDir/$packageDir"
delete "$projectDir/$testsDir"
mkdir("$projectDir/${folderDir}")
mkdir("projectDir/${logDir}")
mkdir("projectDir/${deployDir}")
mkdir("projectDir/${packageDir}")
mkdir("projectDir/${testsDir}")
}
task prepConfigs(type: Copy, overwrite:true, dependsOn: swipe) {
println "The name of ${projectDir}/${folderDir} and ${projectDir}/${configDir}"
from('${projectDir}/${folderDir}')
into('${projectDir}/$configDir}')
include('*.xml')
}
build.properties file:
# -----------------------------------------------------------------
# General Settings
# -----------------------------------------------------------------
application.name = Admin
project.name = Hello Cool
# -----------------------------------------------------------------
# ant build directories
# -----------------------------------------------------------------
sandbox.dir = ${projectDir}/../..
reno.root.dir=${sandbox.dir}/Reno
ant.dir = ${projectDir}/ant
build.dir = ${ant.dir}/build
log.dir = ${ant.dir}/logs
config.dir = ${ant.dir}/configs
deploy.dir = ${ant.dir}/deploy
static.dir = ${ant.dir}/static
package.dir = ${ant.dir}/package
tests.dir = ${ant.dir}/tests
tests.logs.dir = ${tests.dir}/logs
external.dir = ${sandbox.dir}/FlexCommon/External
external.lib.dir = ${external.dir}/libs
If using the default gradle.properties file, you can access the properties directly from within your build.gradle file:
gradle.properties:
applicationName=Admin
projectName=Hello Cool
build.gradle:
task printProps {
doFirst {
println applicationName
println projectName
}
}
If you need to access a custom file, or access properties which include . in them (as it appears you need to do), you can do the following in your build.gradle file:
def props = new Properties()
file("build.properties").withInputStream { props.load(it) }
task printProps {
doFirst {
println props.getProperty("application.name")
println props.getProperty("project.name")
}
}
Take a look at this section of the Gradle documentation for more information.
Edit
If you'd like to dynamically set up some of these properties (as mentioned in a comment below), you can create a properties.gradle file (the name isn't important) and require it in your build.gradle script.
properties.gradle:
ext {
subPath = "some/sub/directory"
fullPath = "$projectDir/$subPath"
}
build.gradle
apply from: 'properties.gradle'
// prints the full expanded path
println fullPath
We can use a separate file (config.groovy in my case) to abstract out all the configuration.
In this example, we're using three environments viz.,
dev
test
prod
which has properties serverName, serverPort and resources. Here we're expecting that the third property resources may be same in multiple environments and so we've abstracted out that logic and overridden in the specific environment wherever necessary:
config.groovy
resources {
serverName = 'localhost'
serverPort = '8090'
}
environments {
dev {
serverName = 'http://localhost'
serverPort = '8080'
}
test {
serverName = 'http://www.testserver.com'
serverPort = '5211'
resources {
serverName = 'resources.testserver.com'
}
}
prod {
serverName = 'http://www.productionserver.com'
serverPort = '80'
resources {
serverName = 'resources.productionserver.com'
serverPort = '80'
}
}
}
Once the properties file is ready, we can use the following in build.gradle to load these settings:
build.gradle
loadProperties()
def loadProperties() {
def environment = hasProperty('env') ? env : 'dev'
println "Current Environment: " + environment
def configFile = file('config.groovy')
def config = new ConfigSlurper(environment).parse(configFile.toURL())
project.ext.config = config
}
task printProperties {
println "serverName: $config.serverName"
println "serverPort: $config.serverPort"
println "resources.serverName: $config.resources.serverName"
println "resources.serverPort: $config.resources.serverPort"
}
Let's run these with different set of inputs:
gradle -q printProperties
Current Environment: dev
serverName: http://localhost
serverPort: 8080
resources.serverName: localhost
resources.serverPort: 8090
gradle -q -Penv=dev printProperties
Current Environment: dev
serverName: http://localhost
serverPort: 8080
resources.serverName: localhost
resources.serverPort: 8090
gradle -q -Penv=test printProperties
Current Environment: test
serverName: http://www.testserver.com
serverPort: 5211
resources.serverName: resources.testserver.com
resources.serverPort: 8090
gradle -q -Penv=prod printProperties
Current Environment: prod
serverName: http://www.productionserver.com
serverPort: 80
resources.serverName: resources.productionserver.com
resources.serverPort: 80
Another way... in build.gradle:
Add :
classpath 'org.flywaydb:flyway-gradle-plugin:3.1'
And this :
def props = new Properties()
file("src/main/resources/application.properties").withInputStream { props.load(it) }
apply plugin: 'flyway'
flyway {
url = props.getProperty("spring.datasource.url")
user = props.getProperty("spring.datasource.username")
password = props.getProperty("spring.datasource.password")
schemas = ['db_example']
}
This is for Kotlin DSL (build.gradle.kts):
import java.util.*
// ...
val properties = Properties().apply {
load(rootProject.file("my-local.properties").reader())
}
val prop = properties["myPropName"]
In Android projects (when applying the android plugin) you can also do this:
import com.android.build.gradle.internal.cxx.configure.gradleLocalProperties
// ...
val properties = gradleLocalProperties(rootDir)
val prop = properties["propName"]
Just had this issue come up today. We found the following worked both locally and in our pipeline:
In build.gradle:
try {
apply from: 'path/name_of_external_props_file.properties'
} catch (Exception e) {}
This way when an external props file which shouldn't get committed to Git or whatever (as in our case) you are using is not found in the pipeline, this 'apply from:' won't throw an error in it. In our use case we have a file with a userid and password that should not get committed to Git. Aside from the problem of file-reading: we found that the variables we had declared in the external file, maven_user and maven_pass, had in fact to be declared in gradle.properties. That is they simply needed to be mentioned as in:
projectName=Some_project_name
version=1.x.y
maven_user=
maven_pass=
We also found that in the external file we had to put single-quotes around these values too or Gradle got confused. So the external file looked like this:
maven_user='abc123'
maven_pass='fghifh7435bvibry9y99ghhrhg9539y5398'
instead of this:
maven_user=abc123
maven_pass=fghifh7435bvibry9y99ghhrhg9539y5398
That's all we had to do and we were fine. I hope this may help others.

Gradle creating duplicate start scripts into bin directory

I am trying to create multiple start script files through gradle. But somehow one particular start script file is getting duplicated.
startScripts.enabled = false
run.enabled = false
def createScript(project, mainClass, name) {
project.tasks.create(name: name, type: CreateStartScripts) {
outputDir = new File(project.buildDir, 'scripts')
mainClassName = mainClass
applicationName = name
classpath = jar.outputs.files + project.configurations.runtime
doLast {
def windowsScriptFile = file getWindowsScript()
def unixScriptFile = file getUnixScript()
windowsScriptFile.text = windowsScriptFile.text.replace('%APP_HOME%\\lib\\conf', '%APP_HOME%\\conf')
unixScriptFile.text = unixScriptFile.text.replace('$APP_HOME/lib/conf', '$APP_HOME/conf')
}
}
project.tasks[name].dependsOn(project.jar)
project.applicationDistribution.with {
into("bin") {
from(project.tasks[name])
fileMode = 0755
}
}
}
// Call this for each Main class you want to expose with an app script
createScript(project, 'com.main.A', 'A')
createScript(project, 'com.main.B', 'B')
in bin directory I can see,
A.sh
A.sh
A.bat
A.bat
B.sh
B.bat
What am I missing here? How to fix this?
Thank you for help.
I solved this problem. Actually it was a mistake from my side and thanks to #Opal. I somehow forgot to delete 'mainClassName="com.main.A"' line from the header.
Also I have to add
distZip {
duplicatesStrategy = 'exclude'
}

Categories