Getting the Gradle.build version into Spring Boot - java

I'm trying to display the application version of my Spring Boot application in a view. I'm sure I can access this version information, I just don't know how.
I tried following this information: https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-endpoints.html, and put this in my application.properties:
info.build.version=${version}
And then loading it #Value("${version.test}") in my controller, but that doesn't work, I only get errors like:
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'version' in string value "${version}"
Any suggestions on what the proper way to get information like my app version, the spring boot version, etc into my controller?

You can also add this in build.gradle :
springBoot {
buildInfo()
}
Then, you can use BuildProperties bean :
#Autowired
private BuildProperties buildProperties;
And get the version with buildProperties.getVersion()

As described in the reference documentation, you need to instruct Gradle to process you application's resources so that it will replace the ${version} placeholder with the project's version:
processResources {
expand(project.properties)
}
To be safe, you may want to narrow things down so that only application.properties is processed:
processResources {
filesMatching('application.properties') {
expand(project.properties)
}
}
Now, assuming that your property is named info.build.version, it'll be available via #Value:
#Value("${info.build.version}")

I've resolved this by adding into application.yml the following:
${version?:unknown}
It also work from cli:gradle bootRun and also from IntelliJ and you don't have to call the Gradle task processResources before launching in IntelliJ or use spring profiles.
This work with Gradle ver:4.6 and also Spring Boot ver: 2.0.1.RELEASE.
Hope it helps ;)

I have solved it this way:
Define your info.build.version in application.properties:
info.build.version=whatever
use it in your component with
#Value("${info.build.version}")
private String version;
now add your version info to your build.gradle file like this:
version = '0.0.2-SNAPSHOT'
then add a method to replace your application.properties with a regex to update your version information there:
def updateApplicationProperties() {
def configFile = new File('src/main/resources/application.properties')
println "updating version to '${version}' in ${configFile}"
String configContent = configFile.getText('UTF-8')
configContent = configContent.replaceAll(/info\.build\.version=.*/, "info.build.version=${version}")
configFile.write(configContent, 'UTF-8')
}
finally, ensure the method is called when you trigger build or bootRun:
allprojects {
updateVersion()
}
that's it. This solution works if you let Gradle compile your app as well as if you run your Spring Boot app from the IDE. The value will not get updated but won't throw an exception and as soon as you run Gradle it will be updated again.
I hope this helps others as well as it solved the problem for me. I couldn't find a more proper solution so I scripted it by myself.

For Kotlin user, what works for me was :
application.properties
In the application.properties you add a placeholder that will be replace with your value by gradle.
project.version= ${version}
build.gradle.kts
Add a task so gradle will replace the value
tasks.processResources { filesMatching("**/application.properties") { expand(project.properties) } }
Service.kt
Inject the value in your services
#Value("\${project.version}") lateinit var version: String

Related

Failed to ugrade a Spring Boot app to Flyway 7.0.0

I'm trying to upgrade my Spring Boot 2.3.4 app to use Flyway 7.0.0 (the latest version). Previously it was using Flyway 6.5.6. The relevant entries in build.gradle are shown below.
buildscript {
ext {
flywayVersion = "7.0.0" // changed from 6.5.6
}
}
plugins {
id "org.flywaydb.flyway" version "${flywayVersion}"
}
dependencies {
implementation "org.flywaydb:flyway-core:${flywayVersion}"
}
flyway {
url = "jdbc:postgresql://0.0.0.0:5432/postgres"
user = "postgres"
password = "secret"
}
The following error occurs when I start the app e.g. with ./gradlew bootRun
APPLICATION FAILED TO START
Description:
An attempt was made to call a method that does not exist. The attempt
was made from the following location:
org.springframework.boot.autoconfigure.flyway.FlywayMigrationInitializer.afterPropertiesSet(FlywayMigrationInitializer.java:65)
The following method did not exist:
'int org.flywaydb.core.Flyway.migrate()'
The method's class, org.flywaydb.core.Flyway, is available from the
following locations:
jar:file:/Users/antonio/.gradle/caches/modules-2/files-2.1/org.flywaydb/flyway-core/7.0.0/623494c303c62080ca1bc5886098ee65d1a5528a/flyway-core-7.0.0.jar!/org/flywaydb/core/Flyway.class
The class hierarchy was loaded from the following locations:
org.flywaydb.core.Flyway: file:/Users/antonio/.gradle/caches/modules-2/files-2.1/org.flywaydb/flyway-core/7.0.0/623494c303c62080ca1bc5886098ee65d1a5528a/flyway-core-7.0.0.jar
Action:
Correct the classpath of your application so that it contains a
single, compatible version of org.flywaydb.core.Flyway
Basically, see Philip's comment on your question.
Flyway 7.x.x is not currently compatible with Spring Boot 2.3.4
Temporary solution is to just downgrade to Flyway 6.5.7 (the last 6.x.x version) until Spring Boot 2.3.5 is released.
Read more and follow the issue here: https://github.com/spring-projects/spring-boot/issues/23514
Support for Flyway's new configuration options: https://github.com/spring-projects/spring-boot/issues/23579
In Flyway 7 the signature of migrate changed.
To get Flyway 7.x.x working with Spring Boot 2.3.x you can provide a custom FlywayMigrationStrategy implementation, which calls the the right migrate method.
import org.flywaydb.core.Flyway;
import org.springframework.boot.autoconfigure.flyway.FlywayMigrationStrategy;
import org.springframework.stereotype.Component;
#Component
public class FlywayMigrationStrategyImpl implements FlywayMigrationStrategy {
#Override
public void migrate(Flyway flyway) {
flyway.migrate();
}
}
downgrade to Flyway 6.5.7 works.

Spring Boot DevTools being used inside docker container even after exclusion in gradle build

So we are using Spring boot to deliver our application. We use the Jib plugin to monitor create docker images and run them.
We use gradle to build the project and there dev tools is identified as a developmentOnly dependency.
Just as mentioned in the spring docs at https://docs.spring.io/spring-boot/docs/current/reference/html/using-spring-boot.html#using-boot-devtools .
However when it runs in the container in prod I still see it getting restarted now and then.
My question is does the gradle configuration not really exlude it from packaging.
Do i need to explicitly set the -Dspring.devtools.restart.enabled=false parameter ?
Solution :
So turns out it was the gradle jib plugin playing games.
While the spring documentation is spot on about how to go about removing the dependency from gradle spring boot project. The technique of specifying a developmentOnly only helps in telling gradle to ignore the dev tools. The jib gradle plugin has a mind of its own.
It includes all jars when building a docker image and there is no way to exclude any jar. The only reasonable way is to customize the gradle jib plugin in build.gradle to write this ,
jib {
from {
image 'gcr.io/distroless/java:11'
}
container {
jvmFlags = ['-Xms1G', '-Xmx1G', '-Dspring.devtools.restart.enabled = false']
}
}
This will make sure that even if the jar is included the container environment has the restart taken care of.
Reference : https://github.com/spring-projects/spring-boot/issues/15382
There's really a few problems here:
Springboot has its own custom definitions instead of using what would be the equivalent of profiles. Their approach is best for springboot users, but rather hard to integrate with given all their custom logic.
Jib can't know all the custom implementation of each framework.
I really think what you should be doing is something like this:
dependencies {
if (System.getProperty("development") == true) {
// include the springboot devtool dependency
}
}
When you want to run or build in dev mode, just do
./gradlew whateverTask -Ddevelopment=true
You can achieve that by setting spring.devtools.restart.enabled=false in your application.properties or your specific profile properties ex. application-cloud.properties. Let me know if this works.
Well, just faced the same problem recently and it seems there's already a very straight-forward way to solve it.
The problem
Jib is actually considering spring-boot-devtools as a runtime dependency and so, adding it to the image. In my case, this is also true for h2 database jar which is used only locally for development.
Also, I wouldn't like to deal with any extra custom parameter in my build, neither turn-off features by configuration if I don't really wanna them available at production.
Solution
GoogleContainerTools' team has released a jib-extension to deal with devtools problem directly. There's a Gradle and Maven version and it works flawlessly.
However, to my needs (also exclude h2) I've decided to use jib layer filter extension so I can keep my image as close to bootJar as possible.
Here goes the code snippet in gradle:
// should be at the top of build.gradle
buildscript {
dependencies {
classpath('com.google.cloud.tools:jib-layer-filter-extension-gradle:0.1.0')
}
}
jib {
// ...
pluginExtensions {
pluginExtension {
implementation = 'com.google.cloud.tools.jib.gradle.extension.layerfilter.JibLayerFilterExtension'
configuration {
filters {
filter {
glob = '**/h2-*.jar'
}
filter {
glob = '**/spring-boot-devtools-*.jar'
}
}
}
}
}
}
Check here the Gradle and Maven version for this extension.

Spring Boot Gradle multi-project build not seeing internal dependencies during tests

I'm having a problem on a larger multi-project build that I was migrating from Gradle 4 to 5, and replicated the issue on a smaller, more concise build to demonstrate the issue.
I have 2 projects in the build. One is a dependency (basic library) used by the other.
demo (root project)
|- build.gradle
|
|--- demo-web
|---|- build.gradle
|
|--- demo-dependency
|---|- build.gradle
Snippet of demo-web: build.gradle
...
dependencies {
implementation project(':demo-dependency')
...
}
...
The dependency project defines one class that is used in the web project, DownstreamThing.
The web project attempts to use that to construct its own object, but during a build on the root project level, it fails.
> ./gradlew build
> Task :demo-web:test
com.example.demo.ThingTest > testThing FAILED
java.lang.NoClassDefFoundError at ThingTest.java:12
Caused by: java.lang.ClassNotFoundException at ThingTest.java:12
ThingTest.java
#Test
public void testThing() {
DownstreamThing t = new DownstreamThing(); //line 12, ClassNotFoundException
assertTrue(t != null);
}
I didn't have any trouble with this in Gradle 4, but only in Gradle 5. Why is the dependency not being found during the test task?
Full source for the example is here: https://bitbucket.org/travelsized/gradle-problem/src/master/
I believe he reason you are getting that exception is because you have applied the Spring Boot plugin to the demo-dependency project. What this plugin does is to repackage the jar file to a fat jar that needs a special classloader to load the content.
You can still use the Spring Boot dependencies (e.g. the starters) in the dependency project without the plugin. So if you can, just remove it.
If there is a particular reason why you have it, you will need to keep the original jar file so that is used as the actual dependency. For Spring Boot 1.5.x, you can do that with something like this:
bootRepackage {
classifier = "boot"
}
bootJar {
enabled = true
}
But do note that I don't think Spring Boot 1.5 is fully compatible with Gradle 5 and later (at this time we are on 6.0), so you might need to either downgrade Gradle or upgrade Spring Boot.
Bjørn Vester's answer got me pointed in the right direction. The spring-boot plugin was causing the jar tasks to go awry. I needed to make sure that the bootJar task was disabled for the dependency while the jar task was enabled.
The changes in the configuration to do this between versions of Gradle and the Spring Boot plugin made this get lost in the upgrades.
Previously, I could specify a classifier for the jar post-boot:
bootRepackage {
classifier = 'exec'
}
Now, I need to enable and disable the appropriate tasks:
bootJar {
enabled = false
}
jar {
enabled = true
archiveBaseName = "demo-dependency"
}
In the larger project, I previously had a jar task that specified the archiveBaseName, but didn't explicitly enable it to override the bootJar task. Once I made the above changes (while keeping the boot plugins in place), things started working.

How to get version variable from build.gradle file into java spring boot controller?

I know how to get it if version is defined in application.properties, but how do I get it from build.gradle?
The general flow is:
Define a property in your application.properties that has placeholders, i.e. gradleVersion=${version}.
Configure Gradle's default task that copies your resource files out to the build directory (called processResources) to filter / expand those properties
Read in the gradleVersion property like any other Spring property
Note that it'll require you to invoke Gradle in order to properly resolve the gradleVersion property (as Gradle is the one putting the value in there). bootRun should already depend on processResources, so if you're using that you should be fine.

Gradle exported properties dont work with gradle bootRun in Spring

in my Spring Boot project i have the following fragment in gradle which exports gradle properties to Spring Environment.
processResources {
filesMatching("**/application.properties") {
expand(project.properties)
}
}
My application.properties looks like this (snippet)
app.version=${jar.version}
Works pretty well. I can work with the gradle propeties in Spring classes with #value and even can access them in thymeleaf with
th:text="${#environment.getProperty('app.version')}
But now the issue: When i run the same project with "gradle bootRun". I am getting this:
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'jar.version' in string value "${jar.version}"
at org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:174) ~[spring-core-4.3.3.RELEASE.jar:4.3.3.RELEASE]
I assume its something about the way gradle's bootRun works by not using processResources or something like that?
The question would be: how can i get this to work. I dont want to give up using bootRun.
Specify a default value to use when the real one cannot be found?
#Value("${...:defaultValue}")

Categories

Resources