Spring - show git commit ID on custom health endpoint - spring

I am trying to show the Git version info (branch, commit etc) on my custom health endpoint.
I tried using management.info.git.mode=full + git-commit-id-plugin but there is no direct way to extract the git info into a Java class. If there is, this will be the ideal way.
I also tried the same git-commit-id-plugin with Value annotations in my Java class like so #Value("${git.commit.id}") but Spring can't find the property values. I see the git.properties file created in the target dir.
What am I missing here? thanks in advance

We have to configure PropertyPlaceHolderConfigurer bean so that we can able to access the property file generated by the plugin, Please use the below code for your reference,
#Bean
public static PropertySourcesPlaceholderConfigurer placeholderConfigurer() {
PropertySourcesPlaceholderConfigurer propsConfig
= new PropertySourcesPlaceholderConfigurer();
propsConfig.setLocation(new ClassPathResource("git.properties"));
propsConfig.setIgnoreResourceNotFound(true);
propsConfig.setIgnoreUnresolvablePlaceholders(true);
return propsConfig;
}
then in your custom health check class, you can use,
#Value("${git.commit.id}")
private String commitId;
I hope this will resolve your problem.

With Spring Boot 2 you can get this information using git-commit-id-plugin in info endpoint. This is how you can configure it
POM File
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>build-info</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>pl.project13.maven</groupId>
<artifactId>git-commit-id-plugin</artifactId>
</plugin>
</plugins>
</build>
Sample Response http://localhost:8080/actuator/info
{
"git":{
"branch":"some-name",
"commit":{
"id":"ef569c2",
"time":1579000598.000000000
}
},
"build":{
"artifact":"xxx",
"name":"xxxx",
"time":1579020527.139000000,
"version":"0.0.1-SNAPSHOT",
"group":"xxxx"
}
}

The easies way is to use the commit plugin:
https://docs.spring.io/spring-boot/docs/current/reference/html/howto.html#howto.build.generate-git-info
It generates git.properties. Then Spring autoconfiguration jumps in. When git.properties is available in the classpath, it creates GitProperties bean:
https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html#actuator.endpoints.info.git-commit-information
Simply inject GitProperties to your bean and use it.

Related

Maven profile filtering with lists

I'm using Maven Filtering to create a property file that would have specialized properties for a given stack.
I have a application.properties file with a property like
elasticsearch.members=${elasticsearch.members}
I have two config files
config.ppd.properties which has
elasticsearch.members=ppd-es-01:9700;ppd-es-02:9700;ppd-es-03:9700
config.prod.properties which has
elasticsearch.members=prod-es-01:9700;prod-es-02:9700;prod-es-03:9700
I have 2 sets of servers with the aliases ppd-es-nn and prod-es-nn
This all works great. When I generate the artifacts using the ppd profile, I get the correct property in the application.properties file.
But what I'd really like to do is have this concatenation somehow done in the application.properties file. So, I could specify the number of elasticsearch servers and a pattern in the application.properties file.
elasticsearch.members=${environment}-es-[index]:9700;...
Is there some way I can achieve that? Perhaps subclassing the class that does the filtering? This example is slightly contrived since I wanted to keep it simple. But one of the things I want to do is append the deployment mode (blue or green) to each server alias. And while I can do that in the filter file, it would be better to do it in the application.properties file so that no typo in the filter class could mess up the alias.
I like the groovy-maven-plugin for tasks like this. The below is untested, but should give you the idea.
<properties>
<!-- these may be overridden on the cmdline -->
<target.env>ppd</target.env>
<es.server.count>2</es.server.count>
<properties>
<plugin>
<groupId>org.codehaus.gmaven</groupId>
<artifactId>groovy-maven-plugin</artifactId>
<version><latestPluginVersionHere></version>
<executions>
<execution>
<id>build-es-host-list</id>
<!-- phase must be earlier than process-resources -->
<phase>validate</phase>
<goals>
<goal>execute</goal>
</goals>
<configuration>
<source>
<![CDATA[
def sb = new StringBuilder()
def targetEnv = project.properties.getProperty('targetEnv')
def numServers = project.properties.getProperty('es.server.count')
(1..numServers).eachWithIndex { it, idx ->
sb.append("${targetEnv}-es-${idx}:9700")
if (idx < numServers) {
sb.append(';')
}
}
project.properties.setProperty('elasticsearch.members', sb.toString())
]]>
</source>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>2.4.12</version>
</dependency>
</dependencies>
</plugin>
Then, the application.properties file line needs to use the property:
elasticsearch.members=${elasticsearch.members}
The POM needs to enable resource filtering for the calculated value to be used in the file.

Externalized configuration for rpm

INTRO:
This question is fairly similar to How to put a directory first on the classpath with Spring Boot? and I've searched around a bit but unfortunately nothing worked so far. Neither changing to packaging=ZIP in the spring-boot-maven-plugin nor using loader.path as explained in boot features external config or how to - properties and configuration, so I must be missing something. One more thing, if possible I would like to avoid passing each and every configuration file as an argument with file://....
I have an application which originally was a simulator for several types of services: sftp, soap, etc. Initially it supported 1 instance of each server type configurable through a properties file.
I've now updated it to support multiple server instances on separate ports, and the configuration is done in a YAML file, and I've also migrated from classic spring to boot. Finally the application is delivered as an RPM, which has the following structure
installation-dir
|--bin
| '-- simulator.sh [lifecycle management script]
|--config
| |--application.properties
| |--log4j2.xml
| |--samples [sample files for various operations]
| | |-- sample1.csv
| | |-- sample2.csv
| | '-- sample3.csv
| |--simulators.yaml [simulators config]
| '--simulator.jks
|--lib
| '-- simulator-1.0.jar
'--log
'-- simulator.log
Before, simulators.sh launched the main class adding the config dir to the classpath, and spring would load the properties file without any issue:
java <other args> -cp "..." com.whatever.SimulatorLauncher
Since the migration, it now launches the generated jar, so the -cp is no longer used, and I'm having trouble making it pick up the configuration files:
java <other args> lib/simulator-1.0.jar
Ignoring for a bit the fact that it does not find aplication.properties, the simulators' config is loaded as below:
#Configuration
#ConfigurationProperties(locations = {"classpath:/simulators.yml"}, ignoreUnknownFields = false, prefix = "simulators")
public class SimulatorSettings {
private static final Logger log = LoggerFactory.getLogger(SimulatorSettings.class);
#NotNull
private List<SftpSettings> sftp;
#NotNull
private List<SoapSettings> soap;
Since the config should be updateable without having to repackage the application, all of the files are excluded from the resulting jar, but packaged in the rpm under config:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.6</version>
<configuration>
<excludes>
<!-- these files will be included in the rpm under config -->
<exclude>application.properties</exclude>
<exclude>log4j2.xml</exclude>
<exclude>samples/**</exclude>
<exclude>simulators.yml</exclude>
<exclude>simulator.jks</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
<configuration>
<mainClass>com.whatever.SimulatorLauncher</mainClass>
<layout>ZIP</layout>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>rpm-maven-plugin</artifactId>
<version>2.1.5</version>
...
<configuration>
...
<mapping>
<directory>${rpm.app.home}/config</directory>
<sources>
<source>
<location>src/main/resources</location>
<includes>
<include>application.properties</include>
<include>simulators.yml</include>
<include>log4j2.xml</include>
<include>nls-sim.jks</include>
</includes>
</source>
</sources>
<filemode>755</filemode>
</mapping>
Any help or hints are greatly appreciated.
I ended up removing the spring-boot-maven-plugin to prevent repackaging, and created the RPM with my artefact & its dependencies and launching the application like before:
java <other args> -cp "<installation dir>/config:...:<libs>" com.whatever.SimulatorLauncher
Still, I'd be interested to find out if anybody knows a way of doing it with a spring boot repackaged jar...

Spring cannot find class path resource

I googled my problem several times. Some of them was good, but not the solution for my problem. Since now, I took hours, to solve my problem.
I started a project in eclipse using maven.
After that, I added spring and hibernate as dependencies.
This project isnt a normal static void main() project.
It is a plugin, which i can implement in an other running program.
Now let me explain my problem:
When I try to start my plugin (FYI: putted it into /plugins folder of the main program), I get a I/O Exception:
org.bukkit.plugin.InvalidPluginException: org.springframework.beans.factory.BeanDefinitionStoreException: IOException parsing XML document from class path resource [applicationContext.xml]; nested exception is java.io.FileNotFoundException:
class path resource [applicationContext.xml] cannot be opened because it does not exist
The applicationContext.xml is in my src/main/resources folder and also in my classpath. I checked it with winRar. After the build process, the applicationContext.xml was in the root directory.
CLICK TO OPEN THE IMAGE
I also use the apache maven-shade-plugin, to include all my dependencys.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<shadedArtifactAttached>true</shadedArtifactAttached>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<manifestEntries>
<mainClass>com.lostforce.core.LostForceCore</mainClass>
</manifestEntries>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
The applicationContext.xml can be found! I checked it with
System.out.println("Is null: " + (getClassLoader().getResourceAsStream("applicationContext.xml") == null));
This returned false for me!
I load the applicationContext.xml with this code:
context = new ClassPathXmlApplicationContext("applicationContext.xml");
I dont know why it happens.
All things I read about this problems, dont work.
My Project:
CLICK TO OPEN THE IMAGE
A little checklist:
- src/main/resource is in my classpath
- applicationContext.xml is in the src/main/resource folder
- build my proect with maven mvn: clean package
- Use the maven-shade-plugin to include dependencys
Hope anyone can help me. Thank you
I solved this problem!
Because my project is a plugin, I have to define a other classloader. So I Created this startup Method:
public class SpringBootstrap {
private final ClassLoader classLoader = SpringBootstrap.class.getClassLoader();
public ApplicationContext startupSpring() {
ApplicationContext context = new ClassPathXmlApplicationContext("application-context.xml") {
protected void initBeanDefinitionReader(XmlBeanDefinitionReader reader) {
super.initBeanDefinitionReader(reader);
reader.setValidationMode(XmlBeanDefinitionReader.VALIDATION_NONE);
reader.setBeanClassLoader(classLoader);
setClassLoader(classLoader);
}
};
return context;
}
}

How to use org.eclipse.jetty.server.Request in a webapp?

In my ServletFilter, I want to use specific jetty API exposed in the HttpServletRequest implementation.
I launched it like that:
final Request jettyRequest = Request.getBaseRequest(request)
If I want to avoid ClassNotFoundException, I must add the jetty-server artifact to my maven dependencies. But if I do that, getBaseRequest returns null because 'request instanceof Request' returns false instead of true.
This is certainly due to conflict between jetty and application classloaders because both of them have loaded 'org.eclipse.jetty.server.Request' class. I tried several configurations, but I was not able to make the Request class exposed to my webapp without adding the dependency in WEB-INF/lib, which causes the classpath issue.
My application is launched with "mvn jetty:run-forked" and configured like this:
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>${jetty-version}</version>
<configuration>
<webAppSourceDirectory>${project.build.directory}/${project.name}</webAppSourceDirectory>
<systemProperties>
<force>true</force>
</systemProperties>
<scanIntervalSeconds>10</scanIntervalSeconds>
<webAppConfig>
<contextPath>/</contextPath>
</webAppConfig>
<jettyXml>../jetty.xml,../jetty-ssl.xml,../jetty-https.xml</jettyXml>
<jvmArgs>-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 -Xbootclasspath/p:${settings.localRepository}/org/mortbay/jetty/alpn/alpn-boot/${alpn-version}/alpn-boot-${alpn-version}.jar</jvmArgs>
</configuration>
</plugin>
Any help will be appreciated!
I fixed the issue by adding a WebAppContext configuration file that contains:
<Configure class="org.eclipse.jetty.webapp.WebAppContext">
<Set name="parentLoaderPriority">true</Set>
</Configure>
The file must be referenced in jetty-maven-plugin like that:
<contextXml>../jetty-context.xml</contextXml>

Can I weave aspect into same maven module it was defined in and into a 3rd party dependency?

I have a multimodule maven project with the following setup of relevant modules:
root
commons-app
backend
frontend
Module frontend is built into war and deployed on Tomcat. Module backend is a standard Java application packaged as jar. All I am trying to accomplish is to make the following aspect work (in both frontend and backend):
#Aspect
public class VirtuosoSequenceSanitizerAspect {
#Around("execution(* cz.cuni.mff.xrg.odcs.commons.app.facade.*Facade.save(..))")
public Object sanitizeSequenceOnSave(ProceedingJoinPoint pjp) throws Throwable {
// ... some code
}
#Before("execution(* org.eclipse.persistence.internal.descriptors.ObjectBuilder.assignSequenceNumber(java.lang.Object, org.eclipse.persistence.internal.sessions.AbstractSession))")
public void rememberAssignSequence(JoinPoint jp) {
// .. some code
}
}
This aspect is setup as a Spring bean in commons-app-context.xml like so:
<!-- enable aspects -->
<aop:aspectj-autoproxy />
<!-- Aspect for fixing corrupted database sequences. -->
<bean id="sequenceAspect" class="cz.cuni.mff.xrg.odcs.commons.app.dao.VirtuosoSequenceSanitizerAspect" />
With this setup the around advice is working properly, however the before advice is not triggered. From what I found I concluded I need to use aspectj-maven-plugin to weave to 3rd party libs. So I added the plugin into the pom.xml for commons-app module like so:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.5</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
<complianceLevel>1.7</complianceLevel>
<showWeaveInfo>true</showWeaveInfo>
<verbose>true</verbose>
<!-- Weave EclipseLink dependency -->
<weaveDependencies>
<weaveDependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>eclipselink</artifactId>
</weaveDependency>
</weaveDependencies>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
<dependencies>
</dependencies>
</plugin>
With this plugin before advice works, but around advice stops working. I have been struggling to set this up correctly so both advices work as expected, but to no avail. When building commons-app module log says both advices are woven:
--- aspectj-maven-plugin:1.5:compile (default) # commons-app ---
Join point 'method-execution(void cz.cuni.mff.xrg.odcs.commons.app.facade.ScheduleFacade.save(cz.cuni.mff.xrg.odcs.commons.app.scheduling.Schedule))' in Type 'cz.cuni.mff.xrg.odcs.commons.app.facade.ScheduleFacade' (ScheduleFacade.java:127) advised by around advice from 'cz.cuni.mff.xrg.odcs.commons.app.dao.VirtuosoSequenceSanitizerAspect' (VirtuosoSequenceSanitizerAspect.java:90)
Join point 'method-execution(void cz.cuni.mff.xrg.odcs.commons.app.facade.DPUFacade.save(cz.cuni.mff.xrg.odcs.commons.app.dpu.DPUTemplateRecord))' in Type 'cz.cuni.mff.xrg.odcs.commons.app.facade.DPUFacade' (DPUFacade.java:123) advised by around advice from 'cz.cuni.mff.xrg.odcs.commons.app.dao.VirtuosoSequenceSanitizerAspect' (VirtuosoSequenceSanitizerAspect.java:90)
Join point 'method-execution(void cz.cuni.mff.xrg.odcs.commons.app.facade.DPUFacade.save(cz.cuni.mff.xrg.odcs.commons.app.dpu.DPUInstanceRecord))' in Type 'cz.cuni.mff.xrg.odcs.commons.app.facade.DPUFacade' (DPUFacade.java:185) advised by around advice from 'cz.cuni.mff.xrg.odcs.commons.app.dao.VirtuosoSequenceSanitizerAspect' (VirtuosoSequenceSanitizerAspect.java:90)
Join point 'method-execution(void cz.cuni.mff.xrg.odcs.commons.app.facade.PipelineFacade.save(cz.cuni.mff.xrg.odcs.commons.app.pipeline.Pipeline))' in Type 'cz.cuni.mff.xrg.odcs.commons.app.facade.PipelineFacade' (PipelineFacade.java:134) advised by around advice from 'cz.cuni.mff.xrg.odcs.commons.app.dao.VirtuosoSequenceSanitizerAspect' (VirtuosoSequenceSanitizerAspect.java:90)
...
However, when I deploy frontend to Tomcat, only the before advice is triggered. How can I configure maven to always weave both advices?
My mistake, I actually found out, that the around advice is being triggered. I did not see this because the code did not do what I expected. Also, I thought it is not triggered because a debugger breakpoint was not hit. From a brief googling I found the reason...
If around advice is inlined, the debugger can't figure out what to do
(we still have some JSR 45 related work to do in this area, and
possibly so does the Eclipse debugger). To debug around advice, you
also need to go to the project properties and turn off the "inline
around advice" AspectJ compiler option. Debugging should then
hopefully work as expected...

Resources