how to capture Build Info using Gradle and Spring Boot - java

I am trying to get access to build info values such as version in my Java main application using Spring Boot and Gradle.
I can't find any documentation / examples of how to configure the
build.gradle
application.yml (if required)
Java main class
could someone please help with a small code example for the above files.
In my build.gradle file I will have the version entry, so how to get this into Java main class using Spring Boot and Gradle.
build.gradle
version=0.0.1-SNAPSHOT
I've tried adding
build.gradle
apply plugin: 'org.springframework.boot'
springBoot {
buildInfo()
}
but the buildInfo() isn't recognised as a keyword in Intellij
In my Java main class I have the following:
public class MyExampleApplication implements CommandLineRunner {
#Autowired
private ApplicationContext context;
public static void main(String[] args) {
SpringApplication.run(MyExampleApplication.class, args);
}
#Override
public void run(String[] args) throws Exception{
Environment env = (Environment) context.getBean("environment");
displayInfo(env);
}
private static void displayInfo(Environment env) {
log.info("build version is <" + env.getProperty("version")
}
But when I run this - the output from env.getProperty("version") is showing as null.

Spring Boot auto-configures a BuildProperties bean with the information generated by buildInfo().
So to get the information use context.getBean(BuildProperties.class).getVersion();.

I managed to get it working now - using the help pointer that Vampire gave below and some other sources. The key was adding the actuator class to the project dependency.
Note: Intellj doesn't seem to recognise buildInfo() in the springBoot tag - but it does run ok - so don't be put off.
build.gradle
buildscript {
ext {
springBootVersion = '1.5.6.RELEASE'
gradleVersion = '3.3'
}
repositories {
mavenLocal()
maven { url "http://cft-nexus.ldn.xxxxxxxxx.com:8081/nexus/content/groups/public/" }
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
apply plugin: 'application'
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: 'org.springframework.boot'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8
springBoot{
buildInfo {
additionalProperties = [
'foo': 'bar'
]
}
}
compile "org.springframework.boot:spring-boot-starter-actuator"
MyExampleApplication
#Slf4j
#EnableIntegration
#EnableLoaderApplication
#SpringBootApplication
#EnableDiscoveryClient
public class MyExampleApplication implements CommandLineRunner {
private static final String SYSTEM_NAME_INFO = "My Example Application";
private static final String VERSION="0.0.1";
#Autowired
private ApplicationContext context;
public static void main(String[] args) {
SpringApplication.run(MyExampleApplication.class, args);
}
#Override
public void run(String[] args) throws Exception{
BuildProperties buildProperties = context.getBean(BuildProperties.class);
displayInfo(buildProperties);
}
private static void displayInfo(BuildProperties buildProperties) {
log.info("build version is <" + buildProperties.getVersion() + ">");
log.info("value for custom key 'foo' is <" + buildProperties.get("foo") + ">");
}
}
Screenshot of Console output when running the Application in Intellj
pasting the output as well incase the image doesn't display
> 2017-11-14 14:35:47.330 INFO 22448 --- [ main]
> o.s.c.support.DefaultLifecycleProcessor : Starting beans in phase
> 2147483647 2017-11-14 14:35:47.448 INFO 22448 --- [ main]
> s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s):
> 8780 (http) 2017-11-14 14:35:47.451 INFO 22448 --- [ main]
> c.u.o.metrics.MyExampleApplication : build version is
> <0.0.1-SNAPSHOT> 2017-11-14 14:35:47.451 INFO 22448 --- [
> main] c.u.o.myexample.MyExampleApplication : value for custom key
> 'foo' is <bar>
UPDATE
After reviewing this with my colleague we decided to move the some of the build properties, e.g. version (above) out of the build.gradle file and into gradle.properties file. This gives us a cleaner separation for build details and properties. When you run Gradle build it automatically pulls these values in and they are available in the BuildProperties bean in the Java main class as shown in the example above.
gradle.properties
group=com.xxx.examplesource
version=0.0.1-SNAPSHOT
gradleVersion=3.3

add following to your Gradle script.It inserts the version into the jar manifest correctly, as shown here:
version = '1.0'
jar {
manifest {
attributes 'Implementation-Title': 'Gradle Quickstart',
'Implementation-Version': version
}
}
Your code will be able to pick up the version from that jar manifest file:
public class BuildVersion {
public static String getBuildVersion(){
return BuildVersion.class.getPackage().getImplementationVersion();
}
}
Refer the link below for more details: https://github.com/akhikhl/wuff/wiki/Manifest-attributes-in-build.gradle

Easy way to get version number in Spring boot
#Controller
#RequestMapping("/api")
public class WebController {
private final BuildProperties buildProperties;
public WebController(BuildProperties properties) {
buildProperties = properties;
}
#GetMapping("/test")
public String index() {
System.out.println(buildProperties.getVersion());
System.out.println(buildProperties.getArtifact());
System.out.println(buildProperties.getGroup());
System.out.println(buildProperties.getName());
System.out.println(buildProperties.getTime());
return "index";
}
}
And dont forget generate application-build.properties
springBoot {
buildInfo()
}

Related

Spring Profiles in Cucumber Testing and Gradle

I have an automation test suite that is using Cucumber and Spring running with a Gradle build. I am facing an issue where depending on environment the property file used will be different. I know there is a way to use Profile annotations to specify ContextConfiguration but I can not find any resources on how to get started on this.
I believe I have to create a ProfileManager Class but I am not sure how to connect this to the #ContectConfiguration annotating my Cucumber Step Definitions and the Property Sources in my Configuration Class.
Here are some of my code samples.
StepDefintions.java
#CucumberContextConfiguration
#ContextConfiguration(classes = {ApplicationConfiguration.class, DynamoDBConfiguration.class})
#TestPropertySource("classpath:application-test-local.properties")
//#TestPropertySource("classpath:application-test.properties")
public class StepDefinition {
#Given("I set stuff up")
public void myGivenStep() {
app.assertTheThings()
#When("I do things")
public void myWhenStep() {
app.checksStuff();
}
.....
}
ApplicationConfiguraton.java
#Configuration
public class ApplicationConfiguration {
#Autowired
private Environment env;
.....
}
build.gradle
{
.....
task cucumber() {
dependsOn assemble, testClasses
doLast {
javaexec {
main='io.cucumber.core.cli.Main'
classpath=configurations.cucumberRuntime + sourceSets.main.output + sourceSets.test.output
def env = System.getProperty('testingENV')
println("Testing in " + env.toUpperCase() + ' Enviornment')
systemProperties = System.properties
def feature = ''
if (project.hasProperty('feature')) {
def arg1 = project.getProperty('feature')
feature = arg1
}
args = [
'--plugin', 'pretty',
'--plugin', 'json:target/cucumber-report.json',
'--glue', 'com.testautomation.cucumber',
"src/test/resources/com/testautomation/cucumber/feature/${feature}"
]
}
}
}
}
Basically I would like to use my testingENV system prop to define the profile and change which property source is being used without having to comment lines out everytime I am switching envs.

Gradle 'implementation' dependency isn't compile with correct code

I have a compilation issue with implementation dependency when compiling with Gradle the following project that contains of 3 modules:
test-impl
test-lib
my-test
My error:
/Users/igor/projects/my-test/src/main/java/MyTest.java:4: error: cannot access TestImpl
lib.foo("");
^
class file for TestImpl not found
It is compiled when I change implemention to api or if I rename any of foo methods in TestLib class.
Gradle 6.0.1, Java 1.8.0_271-b09, OSX
Doesn't it look like a bug? Where to report?
All build.gradle files:
Module test-impl build.gradle:
apply plugin: 'java-library'
Module test-lib build.gradle:
apply plugin: 'java-library'
dependencies {
implementation project(':test-impl')
}
Module my-test build.gradle:
apply plugin: 'java-library'
dependencies {
api project(':test-lib')
}
All source codes:
TestImpl.java in test-impl module:
public class TestImpl {
}
TestLib.java in test-lib module:
public class TestLib {
public void foo(String s) {
}
private void foo(TestImpl impl) {
}
}
MyTest.java in my-test module:
public class MyTest {
public void test() {
TestLib lib = new TestLib();
lib.foo("");
}
}

Microservice- error in bringing up config server org.springframework.boot.context.properties.bind.BindResult

I'm getting below error when trying to bring up config server. Looks incorrect jar version
any suggestions please
Error
Description:
An attempt was made to call a method that does not exist. The attempt was made from the following location:
org.springframework.cloud.config.server.composite.CompositeEnvironmentBeanFactoryPostProcessor.bindProperties(CompositeEnvironmentBeanFactoryPostProcessor.java:81)
The following method did not exist:
'java.lang.Object org.springframework.boot.context.properties.bind.BindResult.orElseCreate(java.lang.Class)'
The method's class, org.springframework.boot.context.properties.bind.BindResult, is available from the following locations:
*jar:file:/C:/Users/GOPU/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot/2.4.0-M1/503562062a9baee246bb7311526d08d16ae05758/spring-boot-2.4.0-M1.jar!/org/springframework/boot/context/properties/bind/BindResult.class*
The class hierarchy was loaded from the following locations:
org.springframework.boot.context.properties.bind.BindResult: file:/C:/Users/GOPU/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot/2.4.0-M1/503562062a9baee246bb7311526d08d16ae05758/spring-boot-2.4.0-M1.jar
Action:
Correct the classpath of your application so that it contains a single, compatible version of org.springframework.boot.context.properties.bind.BindResult
Build.gradle
plugins {
id 'org.springframework.boot' version '2.4.0-M1'
id 'io.spring.dependency-management' version '1.0.9.RELEASE'
id 'java'
}
group = 'com.cloud'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '13'
repositories {
mavenCentral()
maven { url 'https://repo.spring.io/milestone' }
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'org.springframework.cloud:spring-cloud-config-server'
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
implementation 'org.springframework.boot:spring-boot-starter'
}
dependencyManagement {
imports {
mavenBom 'org.springframework.cloud:spring-cloud-dependencies:Hoxton.RELEASE'
}
}
test {
useJUnitPlatform()
}
Mainfile
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.config.server.EnableConfigServer;
#EnableConfigServer
#EnableDiscoveryClient
#SpringBootApplication
public class ConfigApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigApplication.class, args);
}
}

Can't inject a Kotlin service into a Java class

I have the following Kotlin service:
#Service
open class KotlinServiceImpl(private val myRepository: MyRepository) : MyService { ... }
The Java class that's trying to get it:
private final MyService MyService;
public MyController(MyService myService) {
this.myService = myService;
}
The error:
Parameter 0 of constructor in MyController required a bean of type 'MyService' that could not be found.
I'm using Gradle, and have the needed dependencies:
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
implementation 'org.jetbrains.kotlin:kotlin-reflect:1.3.72'
And the relevant Kotlin plugin is also applied:
plugins {
id 'org.jetbrains.kotlin.jvm'
}
When I convert the Java interface MyService to Kotlin, I get the following error:
java.lang.NoClassDefFoundError: com/.../MyService
Using only Java classes solves the problem.
The class wasn't available at runtime. I finally noticed that our build file defines its own run task:
run {
dependsOn pathingJar
doFirst {
classpath = files(
"$buildDir/classes/java/main",
"$buildDir/resources/main",
pathingJar.archivePath)
}
Map<String, String> map = loadExecutionProperties()
map.put("SPRING_PROFILES_ACTIVE", "...")
map.put("APP_NAME", "app-name")
environment(map)
}
So I need to add "$buildDir/classes/kotlin/main" to files in classpath.
I hope this will help future users.

Cannot resolve symbol generated AutoValue_Foo class inside pure java module in android project

I am trying to use AutoValue in a module that is written in pure Java that acts the domain module for my Android application. My application is composed of 3 layers, presentation, domain and data. Presentation and Data both have android dependencies, but domain does not.
Here is the AutoValue class data implementation :
import com.google.auto.value.AutoValue;
import org.jetbrains.annotations.Nullable;
import java.util.Date;
#AutoValue
public abstract class Trip {
public abstract int id();
public abstract String name();
public abstract int totalDistance();
#Nullable public abstract Date startDate();
#Nullable public abstract Date endDate();
public static Builder builder() {
return new AutoValue_Trip.Builder().totalDistance(0);
}
#AutoValue.Builder
public abstract static class Builder {
public abstract Builder id(int id);
public abstract Builder name(String name);
public abstract Builder totalDistance(int totalDistance);
public abstract Builder startDate(Date startDate);
public abstract Builder endDate(Date endDate);
public abstract Trip build();
}
}
Android Studio cannot find the generated AutoValue_Trip class, so it marks it as an error however, the project builds and runs just fine.
Here is my build.gradle of the domain module
buildscript {
repositories {
mavenCentral()
maven {
url "https://plugins.gradle.org/m2/"
}
}
dependencies {
classpath 'me.tatarka:gradle-retrolambda:3.2.3'
classpath "net.ltgt.gradle:gradle-apt-plugin:0.12"
}
}
apply plugin: 'java'
apply plugin: 'idea'
apply plugin: "net.ltgt.apt"
apply plugin: 'me.tatarka.retrolambda'
//noinspection GroovyUnusedAssignment
sourceCompatibility = 1.8
//noinspection GroovyUnusedAssignment
targetCompatibility = 1.8
configurations {
provided
}
sourceSets {
main {
compileClasspath += configurations.provided
}
}
dependencies {
def domainDependencies = rootProject.ext.domainDependencies
def domainTestDependencies = rootProject.ext.domainTestDependencies
apt domainDependencies.autoValue
compileOnly domainDependencies.autoValue
provided domainDependencies.javaxAnnotation
compile domainDependencies.javaxInject
compile domainDependencies.rxJava
compile domainDependencies.arrow
testCompile domainTestDependencies.junit
testCompile domainTestDependencies.mockito
testCompile domainTestDependencies.assertj
}
I have tried using a sourceSet as follows to add the generated build files and folders to the source path:
main {
java {
srcDirs += 'src/../build/generated'
}
}
But then I was getting compileJava errors.
Has anyone ran into this issue and how did you correct it? Additional info will be provided if necessary.

Categories