Sprint-boot-project - I have a requirement to get the version of the spring boot application and port on which it is running - spring-boot

Today i got a requirement, where i have to create a common module to expose the version of the application without changing anything in parent application
Common module
groupId : com.mhn.version, artifactId: version-endpoint packaging: jar
VersionController.java - where i will expose a REST Service "/version" as GET method which returns the details
In any spring boot application if i add this jar (module) as a dependency, then without changing anything in parent application it should fetch the application artifactId and version. Here in this case
1.0.1-SNAPSHOT
For example if i add this as dependency in spring-boot-sample-1.0.1-SNAPSHOT.war application as mentioned in pom.xml below
<groupdId>com.parent.app</groupId>
<artifactId>spring-boot-sample</artifactId>
<version>1.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>com.mhn.version</groupId>
<artifactId>version-endpoint</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
Then spring-boot-sample-1.0.1-SNAPSHOT has to expose a service "/version". and by hitting that endpoint it should return maven project.artifactId and project.version details
In this example
{
"artifactId" : "spring-boot-sample"
"version" : "1.0.1-SNAPSHOT"
}
Guide me, if we have any third party jars, if not guide me on how to do this.
Make this as a note, we are not going to do any changes in parent application

Spring Boot actuators will do this for you with some project configuration as shown in the documentation. Modifying the build configuration to look something like this:
<groupdId>com.parent.app</groupId>
<artifactId>spring-boot-sample</artifactId>
<version>1.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.2.5.RELEASE</version>
<executions>
<execution>
<goals>
<goal>build-info</goal>
</goals>
</execution>
</executions>
...
</plugin>
</plugins>
</build>
Will enable a response from the /actuator/info endpoint that might look like this:
{
"build": {
"artifact": "spring-boot-sample",
"group": "com.parent.app",
"name": "spring-boot-sample",
"time": "2020-03-06T16:29:01.200Z",
"version": "1.0.1-SNAPSHOT"
}
}
If, for some reason, you can't use Boot actuators, you could read the content written by spring-boot-maven-plugin's build-info goal in your own library code by accessing the file as a resource with classpath:META-INF/build-info.properties.

Related

Conflict problem building the Spring boot application

I have a problem building the spring boot application. We need to build the project with the 'lib/bin/conf' structure using the maven. I did it with another project and there is no problem. But now, a conflict occurred and an action is recommended.
Description:
An attempt was made to call a method that does not exist. The attempt was made from the following location:
org.springframework.context.support.GenericApplicationContext.setApplicationStartup(GenericApplicationContext.java:165)
The following method did not exist:
'void org.springframework.beans.factory.support.DefaultListableBeanFactory.setApplicationStartup(org.springframework.core.metrics.ApplicationStartup)'
The method's class, org.springframework.beans.factory.support.DefaultListableBeanFactory, is available from the following locations:
jar:file:/target/OrderManager/libs/communication-latest.jar!/org/springframework/beans/factory/support/DefaultListableBeanFactory.class
jar:file:target/OrderManager/libs/spring-beans-5.3.8.jar!/org/springframework/beans/factory/support/DefaultListableBeanFactory.class
The class hierarchy was loaded from the following locations:
org.springframework.beans.factory.support.DefaultListableBeanFactory: file:target/OrderManager/libs/communication-latest.jar
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory: file:target/OrderManager/libs/communication-latest.jar
org.springframework.beans.factory.support.AbstractBeanFactory: file:target/OrderManager/libs/communication-latest.jar
org.springframework.beans.factory.support.FactoryBeanRegistrySupport: file:target/OrderManager/libs/communication-latest.jar
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry: file:target/OrderManager/libs/communication-latest.jar
org.springframework.core.SimpleAliasRegistry: file:target/OrderManager/libs/communication-latest.jar
Action:
Correct the classpath of your application so that it contains a single, compatible version of org.springframework.beans.factory.support.DefaultListableBeanFactory
How can I solve this problem? I'm using a lib named communication that is provided by our company.
This is my pom file.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example.artifact</groupId>
<artifactId>ordermanager</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Order Manager Component</name>
<properties>
<java.version>11</java.version>
<spring-cloud.version>Hoxton.SR7</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>2.4.2</version>
</dependency>
<dependency>
<groupId>com.excample.myArtifact</groupId>
<artifactId>communication</artifactId>
<version>latest</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>appassembler-maven-plugin</artifactId>
<version>2.1.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>assemble</goal>
</goals>
</execution>
</executions>
<configuration>
<assembleDirectory>target/OrderManager</assembleDirectory>
<repositoryName>libs</repositoryName>
<configurationDirectory>conf</configurationDirectory>
<configurationSourceDirectory>src/main/resources</configurationSourceDirectory>
<copyConfigurationDirectory>true</copyConfigurationDirectory>
<repositoryLayout>flat</repositoryLayout>
<useWildcardClassPath>true</useWildcardClassPath>
<programs>
<program>
<mainClass>com.example.artifact.ordermanager.OrderManagerComponentApplication</mainClass>
<id>OrderManager</id>
</program>
</programs>
</configuration>
</plugin>
</plugins>
</build>
</project>
the answer is already there:
Correct the classpath of your application so that it contains a single, compatible version of org.springframework.beans.factory.support.DefaultListableBeanFactory
Your problem is, that you've got 2 JAR files containing the same class org.springframework.beans.factory.support.DefaultListableBeanFactory. These are:
/target/OrderManager/libs/communication-latest.jar
target/OrderManager/libs/spring-beans-5.3.8.jar
You have to remove one of them. Now I don't know your project and architecture, but if you're using a company customized spring library, then you should remove the standard spring jar. Use maven's exclude mechanism for that, it is a so called transient dependency, so you didn't define it explicitly, but one of the dependencies you defined is dependending on that.
First you have to find out which dependency brings that spring-beans dependency in. Use maven's dependency tree to do that:
mvn dependency:tree
You can also use dependency analysis or read about the whole dependency management.
If you want to kick out the communication-latest.jar you should just remove the following in your pom.xml:
<dependency>
<groupId>com.excample.myArtifact</groupId>
<artifactId>communication</artifactId>
<version>latest</version>
</dependency>
For resolving this conflict you can use excludes-dependencies https://maven.apache.org/guides/introduction/introduction-to-optional-and-excludes-dependencies.html
In this case, it should be
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
from
<groupId>com.excample.myArtifact</groupId>
<artifactId>communication</artifactId>
The problem was with version mismatching with the spring boot that is used in the communication library rather than the spring boot of the main project. So, I changed the spring boot version to 2.3.3.RELEASE and the problem are solved.
Some friends told me to exclude the spring-beans artifact. I did not do that and version changing was enough, But testing the approaches that are told on this topic, can be useful.

Provide Spring Boot git and build information via /actuator/info endpoint when using maven as a build tool

I am using this Spring Boot guide Building a RESTful Web Service with Spring Boot Actuator. When accessing endpoint /actuator/info I am getting empty json response {}.
The actuator api documentation mentions response structures which contain build information like artifact, group, name, version and git information like branch, commit etc.
How can I enable the documented response structures. I want to use maven as build tool (not gradle). This is my pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.7.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>actuator-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>actuator-service</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
After further research I found the answer in the documentation:
Git Information
Add this to plugins section of pom.xml. maven will generate this file during build ./target/classes/git.properties. Spring will read contents of this file and include it in the response of /actuator/info
<plugin>
<groupId>pl.project13.maven</groupId>
<artifactId>git-commit-id-plugin</artifactId>
</plugin>
See Git Commit Information and Generate Git Information
Build Information
Add an execution goal to spring-boot-maven plugin. This will generate the file ./target/classes/META-INF/build-info.properties. Spring will read contents of this file and include it in the response of /actuator/info
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.1.7.RELEASE</version>
<executions>
<execution>
<goals>
<goal>build-info</goal>
</goals>
</execution>
</executions>
</plugin>
Source: Build Information and Generate Build Information
Below is the working solution on Gradle.
Gralde Version 7.3.2
SpringBoot Version: 2.6.1
To include actuators for the project. below dependency should be added to the build.gradle file.
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-actuator'
}
By default only health is available over web. So to enable the info actuator add below entry in your application.yml
management:
endpoints:
web:
exposure:
include: "health,info"
Now when we run the application and attempt to access the /actuator/info end point it prints empty json in response. This is the default behavior of info actuator end point.
To generate the buildInfo from build.gradle, add below in your gradle file
springBoot {
buildInfo()
}
Now if you run the application and hit /actuator/info endpoint, output will be your project's build info
{"build":{"artifact":"actuator-service","name":"","time":"2022-01-12T18:16:28.468Z","version":"0.0.1-SNAPSHOT","group":"com.example"}}
Additional we can configure to generate the git commit information. To do this, you have to apply below plugin
id "com.gorylenko.gradle-git-properties" version "1.5.1"
Once done, on the project build, it will generate a file called git.properties in your build/resources folder.
And now the /actuator/info endpoint will also generated the git information from the git.properties. By default it won't generate all configs from git.properties.
If you want to see full git configuration in /info endpoint, do the below config in application.yml
info:
git:
enabled: true
mode: full
References:
https://docs.spring.io/spring-boot/docs/2.1.7.RELEASE/reference/html/howto-build.html#howto-build-info
https://docs.spring.io/spring-boot/docs/2.1.7.RELEASE/reference/html/howto-build.html#howto-git-info
I had the same problem, /actuator/info always returns {}
First, add plugins (lombok is not necessary):
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
<executions>
<execution>
<goals>
<goal>build-info</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>pl.project13.maven</groupId>
<artifactId>git-commit-id-plugin</artifactId>
</plugin>
Second, go Maven -> compile. Now, in target/classes should be generated git.properties and META-INF folder with build-info.properties.
Finally, run your app and that's it!
You can do that for example by adding the following to your application.properties
info.app.name=#project.name#
info.app.version=#project.version#
info.app.encoding=#project.build.sourceEncoding#
info.app.java.version=#java.version#
Source: https://dzone.com/articles/magic-with-spring-boot-actuator

Import spring boot app into another project

So I am attempting to add a spring boot executable jar as a dependency in another project (Testing framework).
However once added to the pom and imported. Java imports don't work properly. If I look inside the jar all packages are prepended with:
BOOT-INF/classes.some.package.classname.class
There is also some spring boot related packages, MANIFEST etc etc.
Not if I switch the spring boot app's build to just install and deploy a regular jar using the spring-boot-maven-plugin
This changes and everything works fine. Unfortunately this is not a solution for us as we lean on the executable jar as part of our release process.
Can I build a deploy both versions of the jar and use a classifier to determine each?
Thanks
Turns out this exact scenario can be achieved using the spring-boot-maven-plugin.
Spring boot app's pom:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>1.4.1.RELEASE</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
<configuration>
<classifier>exec</classifier>
</configuration>
</execution>
</executions>
...
</plugin>
project using the spring boot jar can be added as normal:
<dependency>
<groupId>com.springboot</groupId>
<artifactId>app</artifactId>
<version>1.0.0-SNAPSHOT</version>
<scope>test</scope>
</dependency>
OR if you want to reference the executible jar
<dependency>
<groupId>com.springboot</groupId>
<artifactId>app</artifactId>
<version>1.0.0-SNAPSHOT</version>
<scope>test</scope>
<classifier>exec</classifier>
</dependency>

Spring boot with maven multi module project

I have a maven multi module project designed like the first answer in following SO post:
Multi-module maven with Spring Boot
Now I want a common maven module that can contain some models to be used by multiple microservices. If I make this common project as a child of the first level parent pom (so that all dependencies injected by boot like jpa, jackson etc are available to common), then STS/Spring is detecting it as a boot application and complains about no Main class on maven build.
Can someone suggest how I can achieve this?
Current Code:
parent pom.xml: (Only relevant parts included)
<project>
<name>...</name>
<groupId>...</groupId>
<artifactId>...</artifactId>
<packaging>pom</packaging>
<version>...</version>
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-parent</artifactId>
<version>Brixton.M3</version>
<relativePath />
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
child (common module) pom.xml (only relevant parts), not to be boot app:
<project>
<artifactId>...</artifactId>
<version>...</version>
<name>...</name>
<parent>
<groupId>...</groupId>
<artifactId>...</artifactId>
<version>...</version>
</parent>
</project>
I don't have all the details regarding your project but my best guess is that the spring-boot-maven-plugin is defined on the parent (or you are using the spring-boot-starter-parent in your root pom). This effectively ask the build to package your module as a Spring Boot app (which is not what you want).
STS probably looks for that hint to figure out if a module contains a Spring Boot application or not. Maybe it would be nicer if it looks for a main class annotated with #EnableAutoConfiguration (or SpringBootApplication).
You can fix the problem easily (from the build side) by specifying the skip property of the repackage goal
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
If STS still picks up the module as a Spring Boot app, I'd create an issue in their tracker
Normally, Spring Boot won't start a web container if it's not present in the module.
I would suggest you to analyse your dendencies using the command
mvn dependency:tree
One more brute-force way of ensuring is use this configuration in your application.properties
spring.main.web-environment=false
Here are two ways to fix this:
You can add the skip property like #Stephane Nicoll mentioned. However, this will completely ignore the test cases inside that module. https://docs.spring.io/spring-boot/docs/current/maven-plugin/examples/it-skip.html
Another option is to add a classifier property to make a separate executable jar out of this module. https://docs.spring.io/spring-boot/docs/current/maven-plugin/examples/repackage-classifier.html
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
<configuration>
<classifier>exec</classifier>
</configuration>
</execution>
This fix will make sure the dependent module get its required jar and the source module will still be an executable one.

Spring-boot application can only be launched with spring-boot:run when forking - java -jar fails

I have a Spring Boot web application that I cannot start when using the executable jar directly.
I am using Spring Boot 1.2.0.RELEASE, Maven 3.0.5, Java 1.7.0_72.
I have a requirement to use the hp-roman8 character set - in order to handle incoming requests from some remote legacy systems. To provide the hp-roman8 charset I use net.freeutils.jcharset in version 1.5.
The jcharset artifact is installed in my local repository
However when launching my application using java -jar the application fails to start and I get "java.nio.charset.UnsupportedCharsetException: hp-roman8" as cause.
The same error occurs if I do mvn spring-boot:run unless I configure spring-boot-maven-plugin to always fork.
With <fork>true</fork> spring-boot:run starts the application successfully and the hp-roman8 charset is available on the classpath.
However <fork>true</fork> has no effect on the created jar, so I am still unable to launch my application using java -jar - and continue to get the "java.nio.charset.UnsupportedCharsetException: hp-roman8".
The jcharset-1.5.jar is included correctly in the created executable jar file next to the rest of the dependencies in the path "lib/jcharset-1.5.jar" so I don't quite understand why it is not available on the classpath when launching the jar.
Have any of you seen similar behavior, or have any ideas as to what I could try out in order to troubleshoot or even resolve this problem?
update:
I have also tried changing the main-class to use the PropertiesLauncher instead (using the <layout>ZIP</layout> tag in the plugin configuration) - see http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#build-tool-plugins-maven-packaging.
Afterwards I added loader.path to my application.properties. Even if I specify the absolute path to jcharset-1.5.jar I still get the UnsupportedCharsetException.
I also tried using an exploded archive but still no go.
You could use Maven's shade plugin rather than Spring Boot's Maven plugin. The main difference is that the shade plugin takes all of your project's dependencies and packages them directly in the jar file, i.e. it doesn't use nested jars. While this has some disadvantages, it does mean that a single class loader is used to load all of your application's classes and, therefore, JCharset is available to the application class loader.
When you're using the Shade plugin, you shouldn't use Spring Boot's starter parent. You may want to import Boot's dependency management instead.
Your pom would look something like this:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-sample-jcharset</artifactId>
<version>0.1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>spring-boot-sample-jcharset</name>
<description>Spring Boot sample showing the use of JCharset in an executable jar</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.7</java.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>1.2.1.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- Any additional dependencies, including JCharset -->
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<manifestEntries>
<Main-Class>sample.jcharset.SampleJCharsetApplication</Main-Class>
</manifestEntries>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

Resources