Passing variables from parameterized Jenkins project using Jenkinsfile, Maven, and Java - java

I have a parameterized Pipeline Jenkins project connected to a Maven project that I forked from https://github.com/jenkins-docs/simple-java-maven-app. I am trying to pass a parameter called "Platform" I have set in the Jenkins Project:
shown here
Before implementing this on my own, larger project, I wanted to see if it was possible to pass a parameter from Jenkins to the Java application via Maven. I've tried some solutions seen in below code.
However, no matter what I try, I still get null when running System.getProperty("platform"). I'm not sure what I could be doing incorrectly. Am I missing something or is there some incorrect syntax I'm just not identifying?
Code snippets below:
Jenkinsfile
pipeline {
agent {
docker {
image 'maven:3-alpine'
args '-v /root/.m2:/root/.m2'
}
}
stages {
stage('Build') {
steps {
sh "mvn -Dplatform=${params.Platform} -B clean package"
}
}
stage('Deliver') {
steps {
sh './jenkins/scripts/deliver.sh'
}
}
}
}
deliver.sh
I added echo "${env.platform}" to see what it returned and I get an error - ./jenkins/scripts/deliver.sh: line 2: ${env.platform}: bad substitution
#!/usr/bin/env bash
echo "${env.platform}"
set -x
mvn jar:jar install:install help:evaluate -Dexpression=project.name
set +x
echo 'The following complex command extracts the value of the <name/> element'
echo 'within <project/> of your Java/Maven project''s "pom.xml" file.'
set -x
NAME=`mvn help:evaluate -Dexpression=project.name | grep "^[^\[]"`
set +x
echo 'The following complex command behaves similarly to the previous one but'
echo 'extracts the value of the <version/> element within <project/> instead.'
set -x
VERSION=`mvn help:evaluate -Dexpression=project.version | grep "^[^\[]"`
set +x
echo 'The following command runs and outputs the execution of your Java'
echo 'application (which Jenkins built using Maven) to the Jenkins UI.'
set -x
java -jar target/${NAME}-${VERSION}.jar
Java main
public static void main(String[] args) {
String test = System.getProperty("platform");
System.out.println(test);
}
pom.xml
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>my-app</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<!-- Build an executable JAR -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
<mainClass>com.mycompany.app.App</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
</project>
UPDATE - solution found:
Followed Илиян Михайлов's solution and it worked! Also, in the Java class, instead of using System.getProperty("platform") I had to use System.getenv("Platform").

You try to set a parameter in the wrong place (in the build step). In maven, each run is independently and not store any information about the parameters. The parameter must be sent when is needed may be on this line mvn jar:jar install:install help:evaluate -Dexpression=project.name -Dplatform="$1" -> this should be sent as parameter from the Jenkins job sh './jenkins/scripts/deliver.sh ${params.Platform}'}

Related

How does maven knows which plugin needs to invoke from the goal?

Let's say
when you call mvn archetype:generate how does maven knows that it needs to invoke "Maven Archetype Plugin"?
Or when you do mvn dependency:copy-dependencies how does it invoke 'Apache Maven Dependency Plugin'?
i.e How does maven maintains the link between 'archetype' -> 'Maven Archetype Plugin'?
It's available by default. This page lists the core plugins and others
https://maven.apache.org/plugins/.
If you want to use other plugin, you need to mention in pom.xml file, so that the dependencies can be resolved.
<build>
<plugins>
<!--Restdocs config for collating all snippets start-->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>animal-sniffer-maven-plugin</artifactId>
<version>XXX</version>
...
</plugin>
</plugins>
</build>
If you run the command mvn animal-sniffer:check. animal-sniffer is the plugin prefix and check is the goal. The mapping between prefix and dependency is mentioned here. Meanwhile, the goal check is mapped by annotation in actual implementation, if you check the source code of this plugin, you will see something like below.
#Mojo( name = "check", defaultPhase = LifecyclePhase.PROCESS_CLASSES, requiresDependencyResolution = ResolutionScope.COMPILE, threadSafe = true )

Similar solutions for java.lang.NoClassDefFoundError: org/joda/time/DateTime not working

I have seen this question posted here before and I have looked at the solutions however I cannot fix the problem I'm having. I created a very simple Maven project in Eclipse for Java and I want to run the output jar file e.g. java -jar jarfilename.jar
I can run the program by right clicking on the project in eclipse and indicating run as Java application. I can build the project to a jar file with mvn package. Running the jar file I get the output of NoClassDefFoundError for the joda time. The joda jar files are in the configured repository e.g. .m2/repository/joda-time/joda-time/2.8.2. There are no errors indicated for the project in Eclipse. I'm using jdk1.8.0_92 Maven version 3.3.9 and eclipse Java EE Neon release 4.6.0. Java home is configured in the environment variables and so too is the class path as: ...\Java\jdk1.8.0_92\jre\lib;C:\Users\username.m2\repository
Some additional information the classpath is correct in terms of not having typos in it. I also looked at a solution from another similar question wherein the suggestion was to add the external jar to the bootstrap entries under run configuratotion. I have also made an entry in the Java build path for joda time which points corretly to the .m2/repository.../joda-time/2.8.2 What this seems like is that when this runs from eclipse the path to the joda time jar file is (for lack of a better term) known. When the jar file is built however that path is not known. I opened the jar file and looked at the MANIFEST.MF file and I see:
Manifest-Version: 1.0
Built-By: John
Class-Path: joda-time-2.8.jar
Build-Jdk: 1.8.0_92
Created-By: Maven Integration for Eclipse
Main-Class: hello.HelloMain
The source is very simple:
package hello;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.format.DateTimeFormat;
public class HelloMain {
public static void main(String[] args) {
System.out.println("Hello From My Main ! It worked\n");
final DateTime today = new DateTime().withZone(DateTimeZone.UTC);
DateTime tommorrow = today.plusDays(3);
String startTime = today.toString(DateTimeFormat.forPattern("yyyy-MM- dd'T'HH:mm'Z"));
String endTime = tommorrow.toString(DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm'Z"));
System.out.printf("The start time %s End Time %s \n", startTime, endTime);
}
}
This is my pom file:
<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>hello</groupId>
<artifactId>hello</artifactId>
<version>0.0.1-SNAPSHOT</version>
<build>
<plugins>
<plugin>
<!-- Build an executable JAR -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.4</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<mainClass>hello.HelloMain</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<!-- https://mvnrepository.com/artifact/joda-time/joda-time -->
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.8</version>
</dependency>
After trying many different approaches to the Pom file I had settled on a different approach. This would only apply if your using Eclipse to do this as Eclipse offers an Export to runnable jar feature. While in Eclipse highlight your project of interest, right click to Export and select Java/Runnable JAR file. Click next and select "Package required libraries into generated JAR". This built the jar file with all of the required jar files I needed. While this really does not solve the problem from the perspective of creating the output jar using the maven packaging it did provide a proper executable jar file.

Running jar built using Maven causes "java.lang.NoClassDefFoundError: org/rosuda/JRI/Rengine" error

I am trying to build a JAR library that can invoke R code.
I basically want this jar to be capable enough to be able to run on any machine that has support for running jar executables(No need of seperate R software).
For this I am using Maven. I am able to compile and create a jar without any errors. However, when I run it, I am unable to yield successful results.
This is my java code
package com.company.analytics.timeseries;
import org.rosuda.JRI.REXP;
import org.rosuda.JRI.Rengine;
public class App {
public static void main(String[] args) {
System.out.println("Creating Rengine (with arguments)");
String[] Rargs = { "--vanilla" };
Rengine re = new Rengine(Rargs, false, null);
System.out.println("Rengine created, waiting for R");
if (!re.waitForR()) {
System.out.println("Cannot load R");
return;
}
System.out.println("Done.");
}
}
This is my pom.xml file
<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>com.company.analytics</groupId>
<artifactId>timeseries</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>timeseries</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.nuiton.thirdparty</groupId>
<artifactId>JRI</artifactId>
<version>RELEASE</version>
</dependency>
<dependency>
<groupId>org.rosuda.REngine</groupId>
<artifactId>REngine</artifactId>
<version>2.1.0</version>
</dependency>
</dependencies>
<repositories>
<repository>
<id>central</id>
<name>Maven Central</name>
<url>http://repo1.maven.org/maven2</url>
</repository>
</repositories>
</project>
I used mvn clean and then mvn package to create the jar file.
A JAR file of 4KB is created in C:\MVN\project\analytics\timeseries\target. Thern, from the command line on Windows, when I run execute this jar file, I get the following error
C:\MVN\project\analytics\timeseries\target\classes>java com.company.analytics.timeseries.App
Creating Rengine (with arguments)
Exception in thread "main" java.lang.NoClassDefFoundError: org/rosuda/JRI/Rengine
at com.company.analytics.timeseries.App.main(App.java:10)
Caused by: java.lang.ClassNotFoundException: org.rosuda.JRI.Rengine
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
... 1 more
I am trying to figure out what mistake am I committing. I tried to find answers by googling, but I couldn't fix it.
Since I've been smashing my head against this for a day now and I'll likely forget in the future and reference this page - per what Gergely Basco's suggests in an above comment, strictly speaking both R and rJava need to be installed on the machine in order to resolve the Cannot find JRI native library! issue when instantiating your org.rosuda.REngine.REngine object, and this cannot be done exclusively by way of adding the JRIEngine dependency in your pom.xml (bummer).
Steps (for how I'm doing it anyway for my later image):
Install Brew (I just happen to be using Brew for other dependencies)
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
Install R using brew:
brew tap homebrew/science
brew install R
Install rJava with R (takes a bit of compile time, grab a coffee)
install.packages("rJava")
add rJava/jri to java.library.path classpath, add R_HOME to environment variables (where you installed R - in my case, where Brew installed it). Note that if you're trying to run this in your IDE(I'm running IDEA16), it won't inherit the path you set in ~/.bash_profile, you need to set it in your run configuration.
-Djava.library.path="/usr/local/lib/R/3.3/site-library/rJava/jri/
R_HOME=/usr/local/Cellar/r/3.3.1_2/R.framework/Resources
Ensure maven has dependency for JRIEngine in pom.xml
<dependency>
<groupId>com.github.lucarosellini.rJava</groupId>
<artifactId>JRIEngine</artifactId>
<version>0.9-7</version>
</dependency>
Instantiate REngine (I need this version in order to pass dataframe to R from java)
String[] Args = {"--vanilla"};
REngine engine = REngine.engineForClass("org.rosuda.REngine.JRI.JRIEngine", Args, new REngineStdOutput (), false);
What you should end up with looks something like this at runtime, if you instantiate with the callback argument (new REngineStdOutput () ); otherwise if you just instantiate with the String engineForClass("org.rosuda.REngine.JRI.JRIEngine"), you'll wont get the below output from R on startup/elsewise, depending on if you want it or not:
/**R version 3.3.1 (2016-06-21) -- "Bug in Your Hair"
Copyright (C) 2016 The R Foundation for Statistical Computing
Platform: x86_64-apple-darwin15.5.0 (64-bit)
R is free software and comes with ABSOLUTELY NO WARRANTY.
You are welcome to redistribute it under certain conditions.
Type 'license()' or 'licence()' for distribution details.
Natural language support but running in an English locale
R is a collaborative project with many contributors.
Type 'contributors()' for more information and
'citation()' on how to cite R or R packages in publications.
Type 'demo()' for some demos, 'help()' for on-line help, or
'help.start()' for an HTML browser interface to help.
Type 'q()' to quit R.**/
Hope this helps someone in the future and saves them from the pain.
You need to build a jar with all your dependencies included. (aka fat jar) Since you are already using Maven, the only thing you need to do is to instruct Maven to include the dependencies by adding this plugin to your pom.xml file:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.5.5</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>assemble-all</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
You are missing the classpath argument. Your jar file contains your compiled code without any 3rd party jars. When you want to run it, you should add -cp and point to all your 3rd party jars.
You can also build a single jar with all dependencies using Maven's assembly plugin.

How to check code coverage with JaCoCo agent?

I am going to deploy application with JaCoCo agent to production environment to let it work for some time. The result should help me identify the parts of code I can get rid of.
I started some research around the topic and prepared HelloWorld application:
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello world");
}
}
Then I compiled the class: "javac HelloWorld.java" and got HelloWorld.class file.
Now I run the app with the following command: "java -javaagent:jacocoagent.jar HelloWorld" the program executes and jacoco binary is generated. The file contains some binary data.
Everything looks fine but the coverage report shows 0% coverage although it should be 100%.
Has anyone faced this issue or correct me what I am doing the bad way?
I generated full report using this steps. Since I use maven for this kind of operations I added maven after your steps. I created HelloWorld.java just copying from your question. Then I follow these steps:
javac HelloWorld.java which outputs HelloWorld.class
Then I created jacoco.exec by executing java -javaagent:jacocoagent.jar HelloWorld
Then I created a pom.xml file which contents are 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>test</groupId>
<artifactId>test</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<name>test</name>
<build>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.7.5.201505241946</version>
</plugin>
</plugins>
</build>
After that I created a target/classes directory. I copied jacoco.exec to target/ and HelloWorld.class to target/classes.
Then I executed mvn jacoco:report which generates a report to target/site/jacoco. Which contains correct coverage information.
I know using maven may not sound good for a simple application. But I don't know any other way to generate reports from jacoco.exec. By the way your maven plugin version and jacocoagent version must match.
And here the result I get.

Debugging Annotation processors in eclipse

I am writing a simple annotation processor and trying to debug it using eclipse. I created a new project for annotation processor and configured javax.annotation.processing.Processor under META-INF as needed and it processes annotations fine.
Then, I added some more code and tried debugging, but could never make the execution stop at the breakpoints added in the annotation processor. I am compiling using ant and I am using the following ANT options.
export ANT_OPTS="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000"
After triggering ant build, i go create a remote debug configuration and the debugger starts fine. Ant build also starts successfully. But the execution never stops at any break point added in the annotation processor.
This is a problem I just ran into, and the eclipse plugin solution seems super cumbersome to me. I found a simpler solution using javax.tools.JavaCompiler to invoke the compilation process. Using the code below, you can just Right-Click > Debug As > JUnit Test in eclipse and debug you annotation processor directly from there
#Test
public void runAnnoationProcessor() throws Exception {
String source = "my.project/src";
Iterable<JavaFileObject> files = getSourceFiles(source);
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
CompilationTask task = compiler.getTask(new PrintWriter(System.out), null, null, null, null, files);
task.setProcessors(Arrays.asList(new MyAnnotationProcessorClass()));
task.call();
}
private Iterable<JavaFileObject> getSourceFiles(String p_path) throws Exception {
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager files = compiler.getStandardFileManager(null, null, null);
files.setLocation(StandardLocation.SOURCE_PATH, Arrays.asList(new File(p_path)));
Set<Kind> fileKinds = Collections.singleton(Kind.SOURCE);
return files.list(StandardLocation.SOURCE_PATH, "", fileKinds, true);
}
This question has been posted over 6 years ago, however, I ran into the same problem now and still couldn't find a good answer on the Internet.
I was finally able to work out a good setup that allows me to develop an Annotation Processor, use it in compilation of another project, and debug it as needed.
The setup is like this:
Annotation Processor developed in a project with GAV:
<groupId>infra</groupId>
<artifactId>annotation-processor</artifactId>
<version>1.0-SNAPSHOT</version>
In the annotation-processor POM file I specified the following:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven.compiler.plugin.version}</version>
<configuration>
<compilerArgument>-proc:none</compilerArgument>
<source>${java.source.version}</source>
<target>${java.source.version}</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
Notice the <compilerArgument>-proc:none</compilerArgument> specification.
In the project where the annotation-processor is used, it is used during the compilation of the project. I.e. the annotation-processor is invoked during the execution of the compiler, javac. I found that in order to debug the annotation-processor execution while running javac directly, I can use the following command line:
javac -J-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=1044 -d target/classes -proc:only -processor infra.annotation.CustomizationAnnotationProcessor -cp ../annotation-processor/target/annotation-processor-1.0-SNAPSHOT.jar src\main\java\org\digital\annotationtest\MyTestClass.java
Notice the suspend=y part in the command line of javac. This tells the JVM to suspend execution until the debugger attaches to it.
In this situation, I can start the eclipse debugger by starting a Remote Java Application Debug Configuration. Configure it to use the annotation-processor project, and attach to the process on localhost and port 1044. this allows you to debug the annotation processor code. If you set a breakpoint in the init or process methods, the debugger will break.
In order to enable the same debug experience while compiling using Maven, I setup the POM file as follows:
Add a dependency to the POM where the annotation-processor is used:
<dependency>
<groupId>infra</groupId>
<artifactId>annotation-processor</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
In the same project using the annotation-processor define the following:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven.compiler.plugin.version}</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<fork>true</fork>
<compilerArgs>
<compilerArg>-J-verbose</compilerArg>
<compilerArg>${enableDebugAnnotationCompilerArg}</compilerArg>
</compilerArgs>
<forceJavacCompilerUse>true</forceJavacCompilerUse>
<annotationProcessorPaths>
<annotationProcessorPath>
<groupId>infra</groupId>
<artifactId>annotation-processor</artifactId>
<version>1.0-SNAPSHOT</version>
</annotationProcessorPath>
</annotationProcessorPaths>
<annotationProcessors>
<annotationProcessor>infra.annotation.CustomizationAnnotationProcessor</annotationProcessor>
</annotationProcessors>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>debugAnnotation</id>
<properties>
<enableDebugAnnotationCompilerArg>-J-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=1044</enableDebugAnnotationCompilerArg>
</properties>
</profile>
</profiles>
Notice the use of <fork>true</fork>,
and <compilerArg>${enableDebugAnnotationCompilerArg}</compilerArg>.
Also, notice the profile deinition of debugAnnotation and the definition of
the <enableDebugAnnotationCompilerArg> property.
This allows us to start a debugging session of the annotation-processor
by running mvn -P debugAnnotation package and attaching the eclipse debugger to the compiler
process the same way as described in 4 above.
The easiest way is to create an eclipse plugin and then debug it directly from eclipse.
It sound a lot harder then it is - this: https://www.youtube.com/watch?v=PjUaHkUsgzo is a 7 minute guide in youtube that can get you started.

Categories

Resources