Read Manifest file in Spring boot application - java

We are using Spring-boot to build micro-services. In my project set-up we have a common maven module named platform-boot with main class with annotation SpringBootApplication
If we want to create a new micro-service (say Service-1), we simply add a dependency of platform-boot module and provide the main-class path in pom.xml and we are good to go.
Problem is when I try to read Manifest.MF file of Service-1 by writing code in my 'main-class' in dependent module. It reads the Manifest.MF file of platform-boot.
Below is the code snippet of how I am reading Manifest.MF file in my main class.
MyMain.class.getProtectionDomain().getCodeSource().getLocation().getPath();
//Returns the path of MyMain.class which is nested jar
Please suggest a way to read the Manifest.MF file of Service-1.
PS: I want to read the Maifest.MF file to get Implementation-Version. Please suggest if any other way of getting it as well.

I found two ways to solve this problem:
We can use maven-dependency-plugin to unpack the child jar while
executing the phase prepare-package. This plug in will extract the
class file from my platform-boot jar to my Service-1.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.0.2</version>
<executions>
<execution>
<id>unpack</id>
<phase>prepare-package</phase>
<goals>
<goal>unpack</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>my.platform</groupId>
<artifactId>platform-boot</artifactId>
<type>jar</type>
<overWrite>false</overWrite>
<outputDirectory>${project.build.directory}/classes</outputDirectory>
<includes>**/*.class,**/*.xml,**/*.text</includes>
<excludes>**/*test.class</excludes>
</artifactItem>
</artifactItems>
<includes>**/*.java, **/*.text</includes>
<excludes>**/*.properties</excludes>
<overWriteReleases>false</overWriteReleases>
<overWriteSnapshots>true</overWriteSnapshots>
</configuration>
</execution>
</executions>
</plugin>
Second approach is much simpler, in spring-boot-maven-plugin add a goal
build-info. This will write a file build-info.properties in your META-INF folder and is accessible in code as below.
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>build-info</goal>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
In your main method you can get this information using the BuildProperties
bean already registered in ApplicationContext.
ApplicationContext ctx = SpringApplication.run(Application.class, args);
BuildProperties properties = ctx.getBean(BuildProperties.class);
In fact, Spring-actuator also uses this BuildProperties to get the build
information which is helpful in monitoring.

Hi could you please elaborate the need of reading your service-1manifest.mf ?
If you just want the service1 as a dependeny in your parent common module and should not conflict with your service1 bootable application , you can do generate two jars through the exec configuration in spring-boot-maven-plugin .

Related

Sending parameters to a maven plugin

In my pom file I execute a build plugin with this configuration.
Can I access customProp from inside the plugin code?
<execution>
...
<configuration>
<configOptions>
<additional-properties>useTags=true</additional-properties>
</configOptions>
<customProp>custom-value</customProp>
Asumming that you are developing the plugin...
Yes, it is possible. Check Parameters section Maven's plugin development guide.
You have to define a property in your Mojo:
#Parameter( property = "your-plugin.customProperty", defaultValue = "custom" )
private String customProperty;
If I understand you correctly, when you configure spring-boot-maven-plugin and building your application, you can access information about your application's build through BuildProperties object like -
#Autowired
BuildProperties buildProperties;
and read like -
// Artifact's name from the pom.xml file
buildProperties.getName();
// Artifact version
buildProperties.getVersion();
If predefined properties are not enough, you can pass your own properties from pom.xml file to BuildProperties
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>build-info</goal>
</goals>
<configuration>
<additionalProperties>
<java.version>${java.version}</java.version>
<some.custom.property>some value</some.custom.property>
</additionalProperties>
</configuration>
</execution>
</executions>
</plugin>
You can pass a value directly or use your custom properties defined in the <properties> section of your pom.xml and then referenced using ${property.name} placeholder.
You can access custom properties defined this way by calling buildProperties.get("property.name").

Use Maven Replace Plugin to replace a phrase on a generated Java file inside Target/generated-sources

I spent hours on this problem, searching several Google and SO entries, I have some ideas but not getting the result.
I have a maven file that does something like this:
grab a jar containing JSON schemas, and unpack them.
Using the Maven Replacer plugin (v 1.5.3), replace a line in a schema file called “MySchema.json” as such:
”Hello” :
”HelloWorld” :
then Maven would use another plugin to compile a class called “converter.java” and runs this class to output a Java file based on “MySchema.json”. let’s call the generated Java file “MyPojo.java”.
Now, I want Maven to replace a line in “MyPojo.java”, but no matter what I do I cannot achieve this.
I tried:
include a separate replace plugin entry for step 4 after the plugin that converts schemas to Java, but ofcourse this caused Maven to complain about existing replace plugin with same artifact/group id from step 2.
Tried adding a separate execution id to the goal “replace” for second plugin, this is invalid for this plugin.
There is a parent project to my current project folder, I tried putting another replacer plugin in the parent POM and make the phase to be any of the “package”, “generate-resources”, “compile”, etc. did not work. Note: the phase for replacements inside “MySchema.json” (in my current project POM) is generate-sources.
give absolute path to the Java, it kept complaining that path does not exist. But I copied and pasted the path to the Java inside windows explorer address bar after it was generated and could read it from Windows explorer. Note that the generated Java file “MyPojo.java”, went under “target/generated-sources” which is sourced by a parent POM above this project using a Maven Helper plugin in parent POM, so this folder should be visible as a source for further compilation. That Maven Helper plugin has phase generate-sources.
Use with same result as above
In my current project (non-parent one) this is the POM code:
<build>
<!—execute a plugin grab schemas jar and unpack schemas-->
...
<plugin>
<groupId>com.google.code.maven-replacer-plugin</groupId>
<artifactId>replacer</artifactId>
<version>1.5.3</version>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>replace</goal>
</goals>
</execution>
</executions>
<configuration>
<includes>
<include>${project.basedir}/target/schemas/MySchema.json</include>
</includes>
<replacements>
<replacement>
<token>"Hello":</token>
<value>"Hello World":</value>
</replacement>
</replacements>
</configuration>
</plugin>
<!-- execute a Plugin for converting shcemas to POJO -->
. . .
</plugins>
</build>
</project>
You should be able to declare the plugin only once and run two replace execution at different Maven Build Lifecycle phases:
Before the Json -> POJO conversion
After the Json -> POJO conversion
So, translating that into could would result in something like:
<plugin>
<!-- (unique) plugin declaration -->
<groupId>com.google.code.maven-replacer-plugin</groupId>
<artifactId>maven-replacer-plugin</artifactId>
<version>1.3.5</version>
<executions>
<!-- first execution: replace on json file -->
<execution>
<id>replace-for-json</id>
<phase>some-phase-before-conversion</phase>
<goals>
<goal>replace</goal>
</goals>
<configuration>
<filesToInclude>${project.basedir}/target/schemas/MySchema.json</filesToInclude>
<preserveDir>true</preserveDir>
<outputDir>target</outputDir>
<replacements>
<replacement>
<token>"Hello":</token>
<value>"Hello World (Json)":</value>
</replacement>
</replacements>
</configuration>
</execution>
<!-- second execution: replace on java file -->
<execution>
<id>replace-for-pojo</id>
<phase>some-phase-after-conversion</phase>
<goals>
<goal>replace</goal>
</goals>
<configuration>
<filesToInclude>${project.basedir}/target/generated-sources/MyPojo.java</filesToInclude>
<preserveDir>true</preserveDir>
<outputDir>target</outputDir>
<replacements>
<replacement>
<token>"Hello":</token>
<value>"Hello World (Java)":</value>
</replacement>
</replacements>
</configuration>
</execution>
</executions>
</plugin>
Source: Configuration for the maven-replacer-plugin on two separate executions

minify frontend with minify-maven-plugin

I'm using the maven plugin minify-maven-plugin in order to minify my frontend project. This works fine when I go over dos box to the frontend project and execute mvn clean install but when I execute mvn clean install in the main pom in my reactor project then I get the following exception:
Failed to execute goal com.samaxes.maven:minify-maven-plugin:1.7.4:minify (default-minify) on project my.project-frontend: Execution default-minify of goal com.samaxes.maven:minify-maven-plugin:1.7.4:minify failed: basedir ./src/main/resources/public/app/. does not exis
Does anyone know what to do in order to make this work?
Below the concerned plugin configuration:
<!-- minify plugin -->
<plugin>
<groupId>com.samaxes.maven</groupId>
<artifactId>minify-maven-plugin</artifactId>
<version>1.7.4</version>
<executions>
<execution>
<id>default-minify</id>
<phase>prepare-package</phase><!-- When omitted defaults to 'process-resources' -->
<configuration>
<charset>UTF-8</charset>
<skipMerge>true</skipMerge>
<nosuffix>true</nosuffix>
<closureCompilationLevel>WHITESPACE_ONLY</closureCompilationLevel>
<webappSourceDir>src/main/resources/public/app</webappSourceDir>
<webappTargetDir>${project.build.outputDirectory}/public/app</webappTargetDir>
<cssSourceDir>./</cssSourceDir>
<cssSourceIncludes>
<cssSourceInclude>**/*.css</cssSourceInclude>
</cssSourceIncludes>
<jsSourceDir>./</jsSourceDir>
<jsSourceIncludes>
<jsSourceInclude>**/*.js</jsSourceInclude>
</jsSourceIncludes>
<jsEngine>CLOSURE</jsEngine>
</configuration>
<goals>
<goal>minify</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- minify plugin end -->
I was able to reproduce your issue and fix it by changing the configuration entry below
<webappSourceDir>src/main/resources/public/app</webappSourceDir>
to
<webappSourceDir>${project.basedir}/src/main/resources/public/app</webappSourceDir>
That is, adding the standard ${project.basedir} property as a prefix.
With that, the build was successful from the module itself but also from the parent one (the reactor/aggregator build).
Thanks to this prefix, the reactor build will properly resolve the path, pointing at the current base directory (the one of the concerned module) during the build.
From official Maven Builder model documentation
{project.basedir} the directory containing the pom.xml file
Hence, the reactor build will replace this property for each module, pointing at the directory containing the module pom.xml file (hence, the directory of the module). It will also work properly when executing the build from the module directly, obviously pointing at the current directory.
Also note: ${basedir} would also work but it is deprecated in favor of project.basedir, hence better to use the latter.
You cam minify as:
<plugin>
<groupId>com.samaxes.maven</groupId>
<artifactId>minify-maven-plugin</artifactId>
<version>1.7.4</version>
<executions>
<execution>
<id>minify-css</id>
<configuration>
<charset>utf-8</charset>
<skipMerge>true</skipMerge>
<statistics>true</statistics>
<cssSourceDir>css</cssSourceDir>
<cssTargetDir>css/min</cssTargetDir>
<cssSourceIncludes>
<cssSourceInclude>*.css</cssSourceInclude>
</cssSourceIncludes>
</configuration>
<goals>
<goal>minify</goal>
</goals>
</execution>
<execution>
<id>minify-js-cfl</id>
<configuration>
<charset>utf-8</charset>
<skipMerge>true</skipMerge>
<statistics>true</statistics>
<jsSourceDir>js/xyz/modules/cfl</jsSourceDir>
<jsTargetDir>js/xyz/modules/cfl/min</jsTargetDir>
<jsSourceIncludes>
<jsSourceInclude>*.js</jsSourceInclude>
</jsSourceIncludes>
</configuration>
<goals>
<goal>minify</goal>
</goals>
</execution>
<execution>

How to include tests in project B from dependent project A and have them recognized by Maven "as is"

I have a (Maven) project B dependent on project A. Project A packages its tests into a jar, as described here. Suppose I have a test class com.forelight.a.FooTest in projecet A. The class is visible on the test-scoped class path in project B, but is not automatically execute by mvn test. I can extend FooTest in project B's test/main/java directory like so:
package com.forelight.b;
public class FooBarTest extends com.forelight.a.FooTest {}
This does the job (mvn test runs this both command line and under eclipse) but feels kludgy.
Here is a working automated solution:
project A should also provide its test-sources jar
project B should import project A in test scope and also import project A test sources in test scope
project B would use the unpack-dependencies of the Maven Dependencies Plugin to automatically unpack the test-sources jar to a subfolder of the target folder (say project-a-test-sources)
project B would use the add-test-source goal of the Build Helper Maven Plugin to add automatically the unpacked sources as test sources in project A
Maven will then compile and run the added sources as part of project B tests
To achieve it, in project A add the following to the build section:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
</plugin>
This will actually create a new jar as part of the build providing test sources. Remember to install it via mvn install.
In project B, add the following to the dependencies:
<dependency>
<groupId>com.sample</groupId>
<artifactId>project-a</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.sample</groupId>
<artifactId>project-a</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>test</scope>
<classifier>test-sources</classifier>
</dependency>
So that the classpath will be populated with project A, the second dependency is harmless, it will be used by the plugin execution below.
In project B, also add the following to the build section:
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.2</version>
<executions>
<execution>
<id>unpack-test-sources</id>
<phase>generate-test-sources</phase>
<goals>
<goal>unpack-dependencies</goal>
</goals>
<configuration>
<includeGroupIds>com.sample</includeGroupIds>
<includeArtifactIds>project-a</includeArtifactIds>
<includeScope>test</includeScope>
<includeClassifiers>test-sources</includeClassifiers>
<outputDirectory>
${project.build.directory}/project-a-test-sources
</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>1.10</version>
<executions>
<execution>
<id>add-test-source</id>
<phase>generate-test-sources</phase>
<goals>
<goal>add-test-source</goal>
</goals>
<configuration>
<sources>
<source>${project.build.directory}/project-a-test-sources</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
Here we are unpacking sources and adding them as test sources.
Maven will then automatically execute the added tests.
Few considerations on this approach:
It might look as kludgy as the approach you mentioned in your question, even though it would be automated and would not require to create new extension tests
It's definitely not something standard, but the original requirement also doesn't sound like a standard practice neither
You may have conflicts on test names or resources names (again, because it's not a standard approach)
You may not want to run these external tests as part of your default build, in this case you could move the configuration above to a Maven profile, say run-project-a-tests and execute them only upon desire via -Prun-project-a-tests. This will also make your default build faster (and more standard).

appassembler-maven-plugin add custom entry to classpath

I'm writing a Java-Application which is using some libaries from anonther thirdparty-Application which is running on my server. At the moment im building my App with the appassembler-maven-plugin. This plugin copy my jars (app and dependencies) into the lib folder an generates a shellscript in the bin dir.
The classpath is generated in this shellscirpt. This solution works but i dublicate the dependency-jars (on time in my app and in the thirdparty-Application write the app for). The classpath of my thirdparty-application is set in a systemvariable like $THIRDPARTYAPP_CLASSPATH.
I want to set the dependencies in my pom.xml to provided, so that the appassembler don't add them to lib and classpath and want to add the systemvar $THIRDPARTYAPP_CLASSPATH in my shellscript, so that my app uses the jars from the installed thirdparty-application.
At the moment i'm doing this manually (editing the shellscript after the build) and it works. Is there any method in the appassembler-maven-plugin to add thid systemvar to the classpath automatically?
I couldn't find anything in the documentation and other questions here regarding a similar problem are not well answerd.
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>appassembler-maven-plugin</artifactId>
<version>1.8.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>assemble</goal>
</goals>
</execution>
</executions>
<configuration>
<assembleDirectory>${project.build.directory}/appassembler</assembleDirectory>
<extraJvmArguments>-Xms512m -Xmx1024m</extraJvmArguments>
<generateRepository>true</generateRepository>
<repositoryName>lib</repositoryName>
<repositoryLayout>flat</repositoryLayout>
<includeConfigurationDirectoryInClasspath>true</includeConfigurationDirectoryInClasspath>
<platforms>
<platform>unix</platform>
</platforms>
<programs>
<program>
<mainClass>${mainClass}</mainClass>
<id>app</id>
</program>
</programs>
</configuration>
</plugin>
You can configure to create an <environmentSetupFileName>setup-env</environmentSetupFileName> which can define a new classpath part via CLASSPATH_PREFIX which should solve your problem.

Categories