Maven Build Profile Activation - java

I know there are some related topics but still I can't understand how to do it.
I'm learning maven and currently in process of creating build profiles. I want maven to auto detect the currently installed java version on my machine. Let's say I'm working in our office which uses (jdk7) or home (jdk8), I want the <source> and <target> elements in maven-compiler-plugin pom.xml to auto detect the java -version regardless of environment (office / home). I've read about activation but can't perfectly understand the purpose.
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>${jdk.version}</source>
<target>${jdk.version}</target>
</configuration>
</plugin>
</plugins>
</build>

Hi I don't think you can have your build, environment aware automatically, just using the defaults, you need some help of profiles and their activation capability see here. What you can do is introduce 2 different profiles, in each profile you can define the JDK you want, and it will be activated for your if present, then you can configure either the compiler plugin with different source /target or, just set different values for a a property that is going to indicate the java version. Example:
<profiles>
<profile>
<id>java8</id>
<activation>
<jdk>1.8</jdk>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>java7</id>
<activation>
<jdk>1.7</jdk>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
Hope that helps :)

For Java 9 the following profiles can be used.
<profiles>
<profile>
<id>java8</id>
<activation>
<jdk>1.8</jdk>
</activation>
(...)
</profile>
<profile>
<id>java9</id>
<activation>
<jdk>9</jdk>
</activation>
(...)
</profile>
</profiles>

You will want to declare two mutually exclusive profiles, one for jdk7 builds and one for jdk8 builds. Each profile declaration has two important parts:
The activation element is like a conditional, and is nested within . Is your profile on or off? You will want to set up the jdk based activation. Example for your jdk7 profile:
<activation>
<jdk>1.7</jdk>
</activation>
Next you'll want to define the property or properties that get set when your profile is active. Example for jdk7:
<properties>
<jdk.version>1.7<jdk.version>
</properties>
Both of those sections get combined with an id element (e.g. <id>jdk7</id>) and nested within a profile element.
If you have any problems getting the jdk detection activation to work, I'd suggest experimenting with an explicit trigger, such as invoking maven with mvn -P jdk7.
From the sounds of it, you want a jdk7 and a jdk8 profile to be defined within your pom.xml. But another option is to have a single profile which is always on, but define the jdk.version property for the profile differently inside of your ~/.m2/settings.xml file. The settings.xml file at the office would have the jdk7 properties; at home it would have jdk8.
http://maven.apache.org/guides/introduction/introduction-to-profiles.html

Related

How to compile in debug mode using maven without making changes to pom.xml?

I am trying to run some vulnerability scans(Veracode) on a spring boot application. However, the scan provider suggests running the scans with binaries compiled in debug mode using the following in pom.xml file.
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<debug>true</debug>
<debuglevel>lines,vars,source</debuglevel>
</configuration>
</plugin>
</plugins>
</build>
However, we use the same pom.xml for production deployment where we don't want debug level jars.
Is there a way to create debug jars by passing some argument to the mvn command?
You can use profiles.
<profiles>
<profile>
<id>debug</id>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<debug>true</debug>
<debuglevel>lines,vars,source</debuglevel>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profile>
Profiles are activated in the mvn command, e.g., mvn ... -Pdebug.
I think it is possible to set the arguments in the command line with -D like this:
mvn compile -Dmaven.compiler.debug=true -Dmaven.compiler.debuglevel=lines,vars,source

Java: set pom.xml property

I have Java Meven project and inside my pom.xml i have this property:
<properties>
<suiteXmlFile>testing.xml</suiteXmlFile>
<JAVA_1_8_HOME>C:\Program Files\Java\jdk1.8.0_181\bin\javac.exe</JAVA_1_8_HOME>
</properties>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<fork>true</fork>
<executable>${JAVA_1_8_HOME}</executable>
</configuration>
</plugin>
So i case i am running my project from Windows i just type mvn test
In case i am with MACOS/Linux this path doesn't exist and i wonder what solutions can be found to fix this issue.
UPDATE
As suggestion here i add this profile:
<profile>
<id>platform-windows</id>
<activation>
<os>
<family>windows</family>
</os>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<fork>true</fork>
<executable>C:\Program Files\Java\jdk1.8.0_181\bin\javac.exe</executable>
</configuration>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>mac</id>
<activation>
<os>
<family>mac</family>
</os>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<fork>true</fork>
<executable>/usr/bin/javac</executable>
</configuration>
</plugin>
</plugins>
</build>
</profile>
Now how my code will now to run this particular id ?
You can make this configuration in two ways:
1) Explicit Profile
Open Maven pom.xml file available in your project directory:
<properties>
<suiteXmlFile>testing.xml</suiteXmlFile>
<JAVA_1_8_HOME>C:\Program Files\Java\jdk1.8.0_181\bin\javac.exe</JAVA_1_8_HOME>
<JAVA_1_8_HOME_LINUX>/usr/java/jdk1.8.0_181/</JAVA_1_8_HOME_LINUX>
</properties>
<profiles>
<!-- Windows Profile-->
<profile>
<id>jdk-8-windows</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<fork>true</fork>
<executable>${JAVA_1_8_HOME}</executable>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</profile>
<!-- Mac/Linux Profile-->
<profile>
<id>jdk-8-linux</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<fork>true</fork>
<executable>${JAVA_1_8_HOME_LINUX}</executable>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
Default active profile was defined as: jdk-8-windows
If the main profile is Mac/Linux, use: <activeProfile>jdk-8linux</activeProfile>
To execute your Mac/Linux profile use: mvn test -P jdk-8-linux
2) Profile Activation via Maven Settings
Open Maven settings.xml file available in %USER_HOME%/.m2 directory where %USER_HOME% represents the user home directory. If settings.xml file is not there, then create a new one.
<settings>
[...]
<profiles>
[...]
<!-- Windows Profile-->
<profile>
<id>jdk-8-windows</id>
<properties>
<JAVA_1_8_HOME>C:\Program Files\Java\j2sdk1.4.2_09</JAVA_1_8_HOME>
</properties>
</profile>
<!-- Mac/Linux Profile-->
<profile>
<id>jdk-8-linux</id>
<properties>
<JAVA_1_8_HOME_LINUX>/usr/bin/javac</JAVA_1_8_HOME_LINUX>
</properties>
</profile>
</profiles>
[...]
<activeProfiles>
<activeProfile>windows</activeProfile>
</activeProfiles>
</settings>
Default active profile was defined as: jdk-8-windows
If the main profile is Mac/Linux, use: <activeProfile>linux</activeProfile>
To execute your Mac/Linux profile use: mvn test -P jdk-8-linux
Reference:
Maven - Build Profiles
Compiling Sources Using A Different JDK
You could pass the property as a command line argument when running the mvn command, for example:
mvn test"-DJAVA_1_8_HOME=<OS specific path>"
For another solutions, take a look at maven condition based on os family
For profiles:
<project>
<profiles>
<profile>
<properties>
// Define profile specific properties here
</properties>
</profile>
</profiles>
</project>
After defining the profile specific properties, use them as you would use any other property.

Different ways to override java version specified in parent pom

I'm using a parent pom which establishes java version to 1.5. In my concrete project I use 1.6 so I was changing the compiler version in eclipse each time I did a Maven Update.
Looking for a solution to this I found some solutions involving overriding the behavior of the parent pom in the child one.
My question is if there are any differences between them and if so, which option should I use. The options I found (perhaps there are more) are:
In properties tag: <app.java.version>1.6</app.java.version>
In properties tag: <jdk.version>1.6</jdk.version>
In configuration tag: <source>${jdk.version}</source>
I'm very new to Maven. Thanks in advance.
You definitely want to go with:
<properties>
<maven.compiler.source>1.6</maven.compiler.source>
<maven.compiler.target>1.6</maven.compiler.target>
</properties>
It is the de facto standard Maven property for setting up Java version and it's used not only by maven-compiler-plugin but also by other standard Maven plugins (including reporting ones), so this applies your Java version globally, not only for compiling classes.
Properties are just properties, which do not mean much unless you use them somewhere.
The important thing is that you set the version in the maven-compiler-pluginconfiguration:
<properties>
<jdk.version>1.6</jdk.version>
</properties>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<configuration>
<source>${jdk.version}</source>
<target>${jdk.version}</target>
</configuration>
</plugin>
</plugins>
If your parent pom uses 1.7 jdk and you want child pom to use 1.6 java version. combine.self="override" is important use following code:
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration combine.self="override">
<verbose>true</verbose>
<fork>true</fork>
<executable>/usr/lib/jvm/java-1.6.0-openjdk-amd64/bin/javac</executable>
<compilerVersion>1.6</compilerVersion>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>

Conditionally adding build extension [duplicate]

My question had been addressed in this thread, but the explanation is not clear.
I have this build definition in one of my pom.xml files:
<build>
<finalName>${my.project}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.0</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
<extensions>
<extension>
<groupId>org.kuali.maven.wagons</groupId>
<artifactId>maven-s3-wagon</artifactId>
<version>1.1.19</version>
</extension>
</extensions>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>**/settings.properties</include>
</includes>
</resource>
</resources>
</build>
Notice that I'm using the maven-s3-wagon extension.
Next, I would like to have 2 different profiles, each with it's own settings, plugins and extensions but maven does not allow the extensions tag under a profile.
When I try using a profile:
<profiles>
<profile>
<id>local-build</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<build>
<finalName>${my.project}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.0</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
<extensions>
<extension>
<groupId>org.kuali.maven.wagons</groupId>
<artifactId>maven-s3-wagon</artifactId>
<version>1.1.19</version>
</extension>
</extensions>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>**/settings.properties</include>
</includes>
</resource>
</resources>
</build>
</profile>
</profiles>
I get a an error in my pom:
cvc-complex-type.2.4.a: Invalid content was found starting with element 'extensions'. One of '{"http://maven.apache.org/POM/4.0.0":defaultGoal, "http://maven.apache.org/POM/
4.0.0":resources, "http://maven.apache.org/POM/4.0.0":testResources, "http://maven.apache.org/POM/4.0.0":directory, "http://maven.apache.org/POM/4.0.0":filters, "http://
maven.apache.org/POM/4.0.0":pluginManagement}' is expected.
Question So using the extension tag means I can't use profiles? How can I use or change build extensions via profile?
Indeed, the official Maven POM reference is not clear about the possible usage of extensions as part of a Maven profile, since it states you can have a build element within it, but not what of the build section.
However, the official Maven model effectively filters and provides what of the build section you can actually use within a profile section. And indeed extensions is not there.
However, what are Maven extensions? Build/Lifecycle enhancement, but also (and essentially): a library added to the runtime classpath of the Maven build, which participates to the build, but it is not packaged with the final artifact.
Hence, in such a scenario (if you need to have extensions in profile or have a profile to change/add an extension) you could use the following trick:
Have an harmless extension as default extension of your build (where harmless means whatever library which could be part of your build classpath and essentially not affect it at all)
Have properties defining the GAV coordinates (GroupId, ArtifactId, Version) of this extension
Have a profile which overrides these properties with the desired (useful) extension
As an example, given the following sample POM:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.sample</groupId>
<artifactId>project</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<extension.groupId>junit</extension.groupId>
<extension.artifactId>junit</extension.artifactId>
<extension.version>4.11</extension.version>
</properties>
<build>
<extensions>
<extension>
<groupId>${extension.groupId}</groupId>
<artifactId>${extension.artifactId}</artifactId>
<version>${extension.version}</version>
</extension>
</extensions>
</build>
<profiles>
<profile>
<id>customize-extension</id>
<properties>
<extension.groupId>junit</extension.groupId>
<extension.artifactId>junit</extension.artifactId>
<extension.version>4.12</extension.version>
</properties>
</profile>
</profiles>
</project>
The default build (without the customize-extension profile activated), would use the default defined properties and as such add junit as build extension: this is harmless (although it may create conflicts with another junit version of your build, so make sure you use the same version of use an even more harmless library for that).
You can check Maven will pick it up by running a really first build phase, just to check information in our case, and enable the debug flag:
mvn initialize -X
And checking as part of the build log:
[DEBUG] Populating class realm extension>junit:junit:4.11
[DEBUG] Included: junit:junit:jar:4.11
Now let's use our trick: let's add (change) a build extension via profile:
mvn initialize -X -Pcustomize-extension
And as part of our build log we would have:
[DEBUG] Populating class realm extension>junit:junit:4.12
[DEBUG] Included: junit:junit:jar:4.12
Bingo. Maven picked up a different extension (in this case, a different version, the 4.12) and we succeeded on changing (or actually adding a meaningful) build extension via profile.
Just a crazy idea: use modules
Define a parent pom like this:
<groupId>org.example</groupId>
<artifactId>my-parent</artifactId>
<version>1.0</version>
<packaging>pom</packaging>
<profiles>
<profile>
<id>use-pom1</id>
<modules>
<module>pom1</module>
</modules>
</profile>
<profile>
<id>use-pom2</id>
<modules>
<module>pom2</module>
</modules>
</profile>
</profiles>
Define the desired extensions on pom1 and pom2.
I think the solution is here http://maven.apache.org/guides/mini/guide-using-extensions.html
Define a build section where extensions are defined and then into the profile set the attribute true ( like in the second profile shown below )
<build>
<extensions>
<extension>
<groupId>org.apache.maven.wagon</groupId>
<artifactId>wagon-ssh</artifactId>
<version>2.9</version>
</extension>
</extensions>
</build>
<profiles>
<profile>
<id>create-default</id>
<activation>
<activeByDefault>true</activeByDefault>
<property>
<name>build</name>
<value>full</value>
</property>
</activation>
<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>
</profile>
<profile>
<id>create-core</id>
<activation>
<property>
<name>build</name>
<value>full</value>
</property>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<extensions>true</extensions>
<version>2.6</version>
<configuration>
<finalName>import-station-core-${project.version}</finalName>
</configuration>
<executions>
<execution>
<id>make-jar</id>
<phase>package</phase>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>

Using JDK8 to compile for target JRE7

Up until today I was under the impression that -target argument on the compile would be enough to ensure that my application would execute on JRE7 even if compiled with a JDK8 javac.
I soon got wiser as I learned about how Sun/Oracle changes method signatures from one release to another.
My aim is - using JDK8 tool chain - to create a binary that will execute with both JRE7 and JRE8. Our build farms where I work are multi-OS, meaning some are Windows, some are Solaris, etc. I cannot predict beforehand where my application is going to build.
I understand the recommended solution is to use -Xbootclasspath on the compile step. I'm using Maven so I'll need something like this:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
<compilerArguments>
<bootclasspath>XXXX</bootclasspath>
</compilerArguments>
</configuration>
</plugin>
</plugins>
</build>
What I don't understand is how to set XXXX so that my application will build anywhere and not just on my own workstation. Essentially I would like XXXX to be an artifact or a dependency if you like. I do not have control over target build machines but I can upload third party artifacts to our corporate Maven repo. How to solve this issue?
The other problem I see is that XXXX is really a list. It is not a single jar. (as far as I understand - to be safe - it is really the value of sun.boot.class.path from the target JRE, meaning it is more than just rt.jar as some literature seems to suggest). How do I set XXXX in a way so that it is OS independent, given that ";" is used as list item separator on Windows whereas ":" is used on Unix/Linux. How to solve that ?
You can try profiles
<profiles>
<profile>
<id>default</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<bootclasspath>xxx</bootclasspath>
</properties>
</profile>
<profile>
<id>win</id>
<activation>
<os>
<family>Windows</family>
</os>
</activation>
<properties>
<bootclasspath>yyy</bootclasspath>
</properties>
</profile>
</profiles>
I suggest not to cross-compile, but to actually use the JDK you want to build against.
If this JDK version differs from the rest of your project, then use the Maven Toolchain Plugin.
Assuming that the maven itself runs under JDK 8 but the intention is to compile a module to be compatible with JDK 7.
The proper cross-compilation requires that the target JDK is present. Consider the following approach to configure cross-compilation for a particular Maven module:
Allow compilation even when the target JDK is not present.
Automatically use cross-compilation when the target JDK is present.
The Maven is unable to detect the presence of other JDKs by itself, so you will have to define the environment property (JDK7_HOME in the example below) which will trigger the cross-compilation profile and provide the base path for bootclasspath configuration.
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<!--
the base config should generate class files
with the proper version number
even in the absence of cross-compilation JDK
-->
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>cross-compile-jdk7</id>
<activation>
<property>
<!--
the proper cross-compilation is triggered
when the cross-compilation JDK is really present
-->
<name>env.JDK7_HOME</name>
</property>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<!--
the advanced config not only generates class files
with the proper version number
but also uses correct bootclasspath
-->
<source>1.7</source>
<target>1.7</target>
<!--
forking is required for when changing bootclasspath
-->
<fork>true</fork>
<!--
the following monstrosity is the most paranoid way
to get correct file and path separators
this particular example includes rt.jar and jsse.jar
from the target JDK
-->
<compilerArgs>
<arg>-bootclasspath</arg>
<arg>${env.JDK7_HOME}${file.separator}jre${file.separator}lib${file.separator}rt.jar${path.separator}${env.JDK7_HOME}${file.separator}jre${file.separator}lib${file.separator}jsse.jar</arg>
</compilerArgs>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>

Categories

Resources