Adding a JNI library to a spring boot (maven) jar - maven

I'm using Google Or-Tools library over a Java-Spring-Boot app, Windows 10 OS and Intellij IDE.
To make it work over intellij I did the following:
Install Microsoft Visual C++ Redistributable for Visual Studio 2019 (required according to the installation instructions).
Downloaded and extracted the OR-Tools library for Java (included 2 jars and a 1 dll file).
In Intellij, I added those jars as module dependencies (under a folder called lib).
Added the lib library path to VM options in Intellij run configurations.
Loaded the library statically in my code:
static {System.loadLibrary("jniortools");}
Now I can run the project successfully form Intellij.
Next I would like to pack everything to a spring boot jar that can run over any windows machine.
My folders structure is:
My pom file is pretty basic, a few dependencies with a standard spring-boot-maven-plugin:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
As I'm trying to pack the code using mvn install I'm getting package com.google.ortools.sat does not exist.
How can I make sure maven packs those 3rd party jars to the executable spring-boot jar?
UPDATE
I added to my pom file:
<dependency>
<groupId>com.google.ortools</groupId>
<artifactId>ortools</artifactId>
<version>LATEST</version>
<type>jar</type>
<scope>system</scope>
<systemPath>${project.basedir}/lib/com.google.ortools.jar</systemPath>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>LATEST</version>
<type>jar</type>
<scope>system</scope>
<systemPath>${project.basedir}/lib/protobuf.jar</systemPath>
</dependency>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.7</version>
<executions>
<execution>
<phase>generate-test-sources</phase>
<configuration>
<tasks>
<mkdir dir="${project.basedir}/target/lib"/>
<echo message="Creating lib folder..."/>
<copy todir="${project.basedir}/target/lib">
<fileset dir="${project.basedir}/lib">
<include name="**/**"/>
</fileset>
</copy>
</tasks>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
In addition adding to library path:
static {
try {
String orToolsDllLibrary = System.getProperty("user.dir") + "\\lib";
addLibraryPath(orToolsDllLibrary);
System.loadLibrary("jniortools");
} catch (Exception e) {
e.printStackTrace();
}
}
public static void addLibraryPath(String pathToAdd) throws Exception {
final Field usrPathsField = ClassLoader.class.getDeclaredField("usr_paths");
usrPathsField.setAccessible(true);
//get array of paths
final String[] paths = (String[]) usrPathsField.get(null);
//check if the path to add is already present
for (String path : paths) {
if (path.equals(pathToAdd)) {
return;
}
}
//add the new path
final String[] newPaths = Arrays.copyOf(paths, paths.length + 1);
newPaths[newPaths.length - 1] = pathToAdd;
usrPathsField.set(null, newPaths);
}
And now when running command java -jar myApp-0.0.1-SNAPSHOT.jar getting an exception:
Caused by: java.lang.NoClassDefFoundError: com/google/ortools/sat/CpSolver
at java.base/java.lang.Class.getDeclaredMethods0(Native Method) ~[na:na]
at java.base/java.lang.Class.privateGetDeclaredMethods(Class.java:3167) ~[na:na]
at java.base/java.lang.Class.getDeclaredMethods(Class.java:2310) ~[na:na]
at org.springframework.util.ReflectionUtils.getDeclaredMethods(ReflectionUtils.java:463) ~[spring-core-5.2.6.RELEASE.jar!/:5.2.6.RELEASE]
... 29 common frames omitted
Caused by: java.lang.ClassNotFoundException: com.google.ortools.sat.CpSolver
at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:471) ~[na:na]
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:588) ~[na:na]
at org.springframework.boot.loader.LaunchedURLClassLoader.loadClass(LaunchedURLClassLoader.java:129) ~[solver-0.0.1-SNAPSHOT.jar:0.0.1-SNAPSHOT]
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521) ~[na:na]
... 33 common frames omitted

I am not sure how you added the library to your project? You don't seem to have done it through Maven, did you?
In the past I took the approach of adding it via using system scope in Maven (see here. This would give you something like this in your pom:
<dependency>
<groupId>com.google.ortools</groupId>
<artifactId>ortools</artifactId>
<version>1.0</version>
<scope>system</scope>
<systemPath>${basedir}/src/main/resources/lib/com.google.ortools.jar</systemPath>
</dependency>
However, this approach can also be a pain especially if you have to work multi-platform. Recently, I found this repo and that made my life much easier dealing with OR-tools. Hope this helps.
UPDATE:
I strongly recommend using the updated method below as it is much less of a headache:
<repositories>
...
<repository>
<id>bintray</id>
<url>https://dl.bintray.com/magneticflux/maven</url>
</repository>
....
</repositories>
<dependencies>
<dependency>
<groupId>com.skaggsm.ortools</groupId>
<artifactId>ortools-natives-all</artifactId>
<version>7.7.7810</version>
</dependency>
<!-- OR-tools needs protobuf -->
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>3.12.2</version>
</dependency>
</dependencies>
Then you can do a static load of the library:
static {
OrToolsHelper.loadLibrary()
}
Make sure to work with JDK >= 11 as elaborated here.

I tried:
First add the jniortools library to java.library.path pragmatically:
static {
String orToolsDllLibrary = System.getProperty("user.dir") + "\\lib";
addLibraryPath(orToolsDllLibrary);
System.loadLibrary("jniortools");
}
public static void addLibraryPath(String pathToAdd) throws Exception {
final Field usrPathsField = ClassLoader.class.getDeclaredField("usr_paths");
usrPathsField.setAccessible(true);
//get array of paths
final String[] paths = (String[]) usrPathsField.get(null);
//check if the path to add is already present
for (String path : paths) {
if (path.equals(pathToAdd)) {
return;
}
}
//add the new path
final String[] newPaths = Arrays.copyOf(paths, paths.length + 1);
newPaths[newPaths.length - 1] = pathToAdd;
usrPathsField.set(null, newPaths);
}
In pom file:
<dependencies>
....
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>3.12.2</version>
</dependency>
<dependency>
<groupId>com.google</groupId>
<artifactId>ortools</artifactId>
<version>0.0.2</version>
</dependency>
....
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
<executions>
<execution>
<id>install-external</id>
<phase>process-resources</phase>
<configuration>
<file>${basedir}/lib/com.google.ortools.jar</file>
<repositoryLayout>default</repositoryLayout>
<groupId>com.google</groupId>
<artifactId>ortools</artifactId>
<version>0.0.2</version>
<packaging>jar</packaging>
<generatePom>true</generatePom>
</configuration>
<goals>
<goal>install-file</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.7</version>
<executions>
<execution>
<phase>generate-sources</phase>
<configuration>
<tasks>
<mkdir dir="${project.basedir}/target/lib"/>
<echo message="Creating lib folder..."/>
<copy todir="${project.basedir}/target/lib">
<fileset dir="${project.basedir}/lib">
<include name="**/**"/>
</fileset>
</copy>
</tasks>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

Related

Allure Report with karate 1.0.1 generating report with just one test case

I am working on a maven project where I am creating both cucumber reports and allure reports with karate 1.0.1 via jenkins. But even though the detailed cucumber reports are getting generated, I am getting only one test case in the allure report
My TestParallelRunner.java file:
#CucumberOptions(plugin = {"pretty" , "html:target/cucumber-html-reports", "io.qameta.allure.cucumber4jvm.AllureCucumber5Jvm","json:target/cucumber/cucumber.json"})
//#KarateOptions(tags = "~#ignore")
public class TestParallelRunner {
#Test
public void testParallel() {
//String outputDir = "target//surefire-reports";
Builder testRun = new Builder();
testRun.path("classpath:com/api/automation/Features").outputCucumberJson(true).tags("~#ignore");
Results results = testRun.parallel(3);
generateReport(results.getReportDir());
Assertions.assertEquals(0, results.getFailCount(), "There are some Failed Scenarios");
}
public static void generateReport(String reportDirLocation) {
File reportDir=new File(reportDirLocation);
Collection<File> jsonFiles = FileUtils.listFiles(reportDir, new String[] {"json"}, true);
//jsonFiles.add(File("cucumber-report.json"));
List<String> jsonPaths = new ArrayList<>();
//jsonFiles.add("cucumber-report-2.json");
jsonFiles.forEach(file -> jsonPaths.add(file.getAbsolutePath()));
Configuration config = new Configuration(new File("target"), "Cucumber Report");
ReportBuilder reportBuilder = new ReportBuilder(jsonPaths, config);
reportBuilder.generateReports();
}
}
My pom.xml file:
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>11</java.version>
<maven.compiler.version>3.8.1</maven.compiler.version>
<maven.surefire.version>2.22.2</maven.surefire.version>
<karate.version>1.0.1</karate.version>
<allure.maven.version>2.11.2</allure.maven.version>
<allure-junit5.version>2.17.3</allure-junit5.version>
</properties>
<dependencies>
<dependency>
<groupId>com.intuit.karate</groupId>
<artifactId>karate-junit5</artifactId>
<version>${karate.version}</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/net.masterthought/cucumber-reporting -->
<dependency>
<groupId>net.masterthought</groupId>
<artifactId>cucumber-reporting</artifactId>
<version>5.6.1</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/io.qameta.allure/allure-maven -->
<dependency>
<groupId>io.qameta.allure</groupId>
<artifactId>allure-maven</artifactId>
<version>${allure.maven.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.qameta.allure/allure-cucumber5-jvm -->
<dependency>
<groupId>io.qameta.allure</groupId>
<artifactId>allure-cucumber5-jvm</artifactId>
<version>2.17.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.qameta.allure/allure-junit5 -->
<dependency>
<groupId>io.qameta.allure</groupId>
<artifactId>allure-junit5</artifactId>
<version>${allure-junit5.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<testResources>
<testResource>
<directory>src/test/java</directory>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</testResource>
</testResources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven.compiler.version}</version>
<configuration>
<encoding>UTF-8</encoding>
<source>${java.version}</source>
<target>${java.version}</target>
<compilerArgument>-Werror</compilerArgument>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven.surefire.version}</version>
<executions>
<execution>
<goals>
<goal>test</goal>
</goals>
</execution>
</executions>
<configuration>
<useSystemClassLoader>false</useSystemClassLoader>
<testFailureIgnore>true</testFailureIgnore>
<systemProperties>
<property>
<name>allure.results.directory</name>
<value> ${project.build.directory}/allure-results</value>
</property>
</systemProperties>
</configuration>
</plugin>
</plugins>
</build>
</project>
My Allure reports in my jenkins pipeline:
allure([
includeProperties: false,
jdk: '',
properties:[],
reportBuildPolicy:'ALWAYS',
results: [[path: '/allure-results']]
]
)
But my the json file created in my allure-results folder contains only the following entry:
{"uuid":"XXXXXXX-XXXXX-XXXXXX","historyId":"XXXXXXXXXXXXXXXXXX","testCaseId":"[engine:junit-jupiter]/[class:com.api.automation.TestParallelRunner]/[method:testParallel()]","testCaseName":"testParallel()","fullName":"com.api.automation.TestParallelRunner.testParallel","labels":[{"name":"junit.platform.uniqueid","value":"[engine:junit-jupiter]/[class:com.api.automation.TestParallelRunner]/[method:testParallel()]"},{"name":"host","value":"XXXXXX"},{"name":"thread","value":"XXXXXXX.main(1)"},{"name":"framework","value":"junit-platform"},{"name":"language","value":"java"},{"name":"package","value":"com.api.automation.TestParallelRunner"},{"name":"testClass","value":"com.api.automation.TestParallelRunner"},{"name":"testMethod","value":"testParallel"},{"name":"suite","value":"com.api.automation.TestParallelRunner"}],"links":[],"name":"testParallel()","status":"passed","stage":"finished","description":"","steps":[],"attachments":[],"parameters":[],"start":1652145767274,"stop":1652145775326}
Thus not getting a complete test execution picture the way cucumber report is showing :
A few points:
#CucumberOptions is not supported in Karate, read the docs: https://github.com/karatelabs/karate#parallel-execution
ensure that you tell Karate to emit the Cucumber JSON: https://github.com/karatelabs/karate/wiki/1.0-upgrade-guide#java-projects
for good measure use the latest version (1.2.0) as of now
Also refer: https://stackoverflow.com/a/54527955/143475

FlatMapElement Kotlin Beam non Serializable lambda

I have an existing Apache Beam project with Java 8, Apache Beam 2.27.0, Maven and Dagger 2.
I migrated this project in Kotlin : Kotlin JDK 8 with version 1.5.0.
I used the 1.5.0 version of Kotlin because the 1.4.3 had an issue with Beam and Maven plugin (Could not read class: VirtualFile : Kotlin 1.4.30 Apache beam compilation error)
Everything seems to be good except the use of native MapElement or FlatMapElement with Typedescriptor and lambda expression.
A part of my pom.xml file
<properties>
<beam.version>2.27.0</beam.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<kotlin.code.style>official</kotlin.code.style>
<kotlin.compiler.jvmTarget>1.8</kotlin.compiler.jvmTarget>
<kotlin.compiler.incremental>true</kotlin.compiler.incremental>
<kotlin.version>1.5.0</kotlin.version>
<serialization.version>1.2.0</serialization.version>
<java.version>1.8</java.version>
<dagger.version>2.35.1</dagger.version>
<maven-compiler-plugin.version>3.7.0</maven-compiler-plugin.version>
<maven-exec-plugin.version>1.6.0</maven-exec-plugin.version>
<maven-surefire-plugin.version>3.0.0-M5</maven-surefire-plugin.version>
<properties>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId>
<version>${kotlin.version}</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-serialization-json</artifactId>
<version>${serialization.version}</version>
</dependency>
<dependency>
<groupId>org.apache.beam</groupId>
<artifactId>beam-runners-google-cloud-dataflow-java</artifactId>
<version>${beam.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.beam</groupId>
<artifactId>beam-sdks-java-core</artifactId>
<version>${beam.version}</version>
</dependency>
<dependency>
<groupId>org.apache.beam</groupId>
<artifactId>beam-sdks-java-io-google-cloud-platform</artifactId>
<version>${beam.version}</version>
</dependency>
<dependency>
<groupId>org.apache.beam</groupId>
<artifactId>beam-sdks-java-io-redis</artifactId>
<version>${beam.version}</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-test-junit</artifactId>
<version>${kotlin.version}</version>
<scope>test</scope>
</dependency>
<dependencies>
<build>
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>${kotlin.version}</version>
<executions>
<execution>
<id>kapt</id>
<goals>
<goal>kapt</goal>
</goals>
<configuration>
<sourceDirs>
<sourceDir>src/main/kotlin</sourceDir>
</sourceDirs>
<annotationProcessorPaths>
<annotationProcessorPath>
<groupId>com.google.dagger</groupId>
<artifactId>dagger-compiler</artifactId>
<version>${dagger.version}</version>
</annotationProcessorPath>
</annotationProcessorPaths>
</configuration>
</execution>
<execution>
<id>compile</id>
<phase>process-sources</phase>
<goals>
<goal>compile</goal>
</goals>
<configuration>
<sourceDirs>
<sourceDir>src/main/kotlin</sourceDir>
</sourceDirs>
</configuration>
</execution>
<execution>
<id>test-kapt</id>
<goals>
<goal>test-kapt</goal>
</goals>
<configuration>
<sourceDirs>
<sourceDir>src/test/kotlin</sourceDir>
</sourceDirs>
<annotationProcessorPaths>
<annotationProcessorPath>
<groupId>com.google.dagger</groupId>
<artifactId>dagger-compiler</artifactId>
<version>${dagger.version}</version>
</annotationProcessorPath>
</annotationProcessorPaths>
</configuration>
</execution>
<execution>
<id>test-compile</id>
<goals>
<goal>test-compile</goal>
</goals>
<configuration>
<sourceDirs>
<sourceDir>src/test/kotlin</sourceDir>
<sourceDir>target/generated-sources/kapt/test</sourceDir>
</sourceDirs>
</configuration>
</execution>
</executions>
<configuration>
<compilerPlugins>
<plugin>kotlinx-serialization</plugin>
</compilerPlugins>
</configuration>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-serialization</artifactId>
<version>${kotlin.version}</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven-surefire-plugin.version}</version>
<dependencies>
<dependency>
<groupId>org.apache.maven.surefire</groupId>
<artifactId>surefire-junit47</artifactId>
<version>${maven-surefire-plugin.version}</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>${maven-exec-plugin.version}</version>
<configuration>
<cleanupDaemonThreads>false</cleanupDaemonThreads>
</configuration>
</plugin>
</plugins>
An object that implements Serializable (java.io)
data class MyObject(
val field: String = ""
) : Serializable {
And basically i want to execute a FlatMapElement with Typedescriptor and a lambda (behind the scene a SerializableFunction)
class MyTransform(private val redisConnectionConf: RedisConnectionConfiguration) :
PTransform<PBegin, PCollection<MyObject>>() {
override fun expand(input: PBegin): PCollection<MyObject> {
return input
.apply(RedisIO.read().withConnectionConfiguration(redisConnectionConf).withKeyPattern("my-pattern*"))
.apply(
FlatMapElements.into(of(MyObject::class.java))
.via(SerializableFunction<KV<String, String>, List<MyObject>> { toMyObjects(it) })
)
}
fun toMyObjects(entry: KV<String, String>): List<MyObject> {
val key = entry.key
val value = entry.value
val ref = object : TypeReference<List<MyObject>>() {}
return OBJECT_MAPPER.readValue(value, ref)
}
I volontary changed the code and put some part of code in method "toMyObjects" in order to give the maximum of elements.
The "OBJECT_MAPPER" object is a Jackson Object Mapper.
With Java 8 and Beam 2.27.0 this basic code works perfectly fine.
With Kotlin this code doesn't works with the following error :
at org.apache.beam.sdk.util.SerializableUtils.serializeToByteArray (SerializableUtils.java:59)
at org.apache.beam.runners.core.construction.ParDoTranslation.translateDoFn (ParDoTranslation.java:692)
at org.apache.beam.runners.dataflow.PrimitiveParDoSingleFactory$PayloadTranslator$1.translateDoFn (PrimitiveParDoSingleFactory.java:218)
at org.apache.beam.runners.core.construction.ParDoTranslation.payloadForParDoLike (ParDoTranslation.java:814)
at org.apache.beam.runners.dataflow.PrimitiveParDoSingleFactory$PayloadTranslator.payloadForParDoSingle (PrimitiveParDoSingleFactory.java:214)
at org.apache.beam.runners.dataflow.PrimitiveParDoSingleFactory$PayloadTranslator.translate (PrimitiveParDoSingleFactory.java:163)
at org.apache.beam.runners.core.construction.PTransformTranslation$KnownTransformPayloadTranslator.translate (PTransformTranslation.java:429)
at org.apache.beam.runners.core.construction.PTransformTranslation.toProto (PTransformTranslation.java:239)
at org.apache.beam.runners.core.construction.SdkComponents.registerPTransform (SdkComponents.java:175)
at org.apache.beam.runners.core.construction.PipelineTranslation$1.visitPrimitiveTransform (PipelineTranslation.java:87)
at org.apache.beam.sdk.runners.TransformHierarchy$Node.visit (TransformHierarchy.java:587)
at org.apache.beam.sdk.runners.TransformHierarchy$Node.visit (TransformHierarchy.java:579)
at org.apache.beam.sdk.runners.TransformHierarchy$Node.visit (TransformHierarchy.java:579)
at org.apache.beam.sdk.runners.TransformHierarchy$Node.visit (TransformHierarchy.java:579)
at org.apache.beam.sdk.runners.TransformHierarchy$Node.access$500 (TransformHierarchy.java:239)
at org.apache.beam.sdk.runners.TransformHierarchy.visit (TransformHierarchy.java:213)
at org.apache.beam.sdk.Pipeline.traverseTopologically (Pipeline.java:468)
at org.apache.beam.runners.core.construction.PipelineTranslation.toProto (PipelineTranslation.java:59)
at org.apache.beam.runners.dataflow.DataflowRunner.run (DataflowRunner.java:933)
at org.apache.beam.runners.dataflow.DataflowRunner.run (DataflowRunner.java:196)
at org.apache.beam.sdk.Pipeline.run (Pipeline.java:322)
at org.apache.beam.sdk.Pipeline.run (Pipeline.java:308)
at myPackage.MyApp.main (MyApp.kt:44)
at sun.reflect.NativeMethodAccessorImpl.invoke0 (Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke (Method.java:498)
at org.codehaus.mojo.exec.ExecJavaMojo$1.run (ExecJavaMojo.java:282)
at java.lang.Thread.run (Thread.java:748)
Caused by: java.io.NotSerializableException: Non-serializable lambda
at mypackage.MyTransform$$Lambda$783/1784079343.writeObject (Unknown Source)
[ERROR] Failed to execute goal org.codehaus.mojo:exec-maven-plugin:1.6.0:java (default-cli) on project my-project:
An exception occured while executing the Java class. unable to serialize
DoFnWithExecutionInformation{doFn=org.apache.beam.sdk.transforms.FlatMapElements$2#23402e70,
mainOutputTag=Tag<org.apache.beam.sdk.values.PCollection.<init>:402#6929f09b03d242ca>, sideInputMapping={}, schemaInformation=DoFnSchemaInformation{elementConverters=[]}}: Non-serializable lambda -> [Help 1]
The SerializableUtils.serializeToByteArray method in Beam sdk sends this error : java.io.NotSerializableException: Non-serializable lambda
MyObject is Serializable and the lambda is wrapped in a Beam SerializableFunction (function that implements Serializable).
Normally in this case, Beam take a SerializableCoder from the Serializable object.
I don't understand why Beam saw the lambda as non Serializable.
I don't have this kind of behaviour directly in Java.
I precise, if i replace the FlatMapElement/descriptor/lambda by a ParDo.of(DoFn), this works fine, but in some cases for a better concision and readabilty, i want to use the built in MapElement and FlatMapElement with lambda expressions.
Thanks in advance for your help.
Finally i found the solution, i downgraded the Kotlin version (dependencies + plugin) to 1.4.21.
In this case the problem of Lambda non Serializable disapeared and the kotlin Maven plugin doesn't have the virtual file problem at compile time : Kotlin 1.4.30 Apache beam compilation error
This topic helped me a lot, thanks : https://youtrack.jetbrains.com/issue/KT-45067
Maybe in the future it would be great, if the Kotlin maven plugin works correctly with the version 1.4.x greater than 1.4.21.
Beam developers with Kotlin and Maven must be carreful with this issue, 1.4.32 doesn't compile with Beam and 1.5.0 have a problem at runtime with Lambda non Serializable.
Try this workaround if you want to use Kotlin 1.5 :
use -Xsam-conversions=class
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>${kotlin.version}</version>
<configuration>
<args>
<arg>-Xsam-conversions=class</arg>
</args>
</configuration>
</plugin>
</plugins>
Reference : https://youtrack.jetbrains.com/issue/KT-46359#focus=Comments-27-4862857.0-0
When i replace the lambda by a class that implements the SerializableFunction function, this works
class MapString : SerializableFunction<KV<String, String>, List<MyObject>> {
override fun apply(input: KV<String, String>): List<MyObject> {
....
}
}
I keep the issue opened because i want to have a solution working with lambda expressions.

maven plugin descriptor not getting generated

I'm creating a maven plugin, MVN clean install build succeeds but plugin.xml is not getting generated.
#Mojo( name = "cover", defaultPhase = LifecyclePhase.POST_INTEGRATION_TEST)
public class RunCoverage extends AbstractMojo
{
#Parameter( property = "cover.wadl", defaultValue = "test")
private String wadl;
#Parameter( property = "cover.endpoints",defaultValue = "test")
private String endpoints;
#Override
public void execute() throws MojoExecutionException
{
<somecode>
}
}
And the pom.xml is
<?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>
<artifactId>end-point-test-coverage</artifactId>
<version>1</version>
<dependencies>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-plugin-api</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>org.apache.maven.plugin-tools</groupId>
<artifactId>maven-plugin-annotations</artifactId>
<version>3.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-plugin-plugin</artifactId>
<version>3.2</version>
<executions>
<execution>
<id>default-descriptor</id>
<goals>
<goal>descriptor</goal>
</goals>
<phase>process-classes</phase>
</execution>
<execution>
<id>help-descriptor</id>
<goals>
<goal>helpmojo</goal>
</goals>
<phase>process-classes</phase>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.0.2</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
Maven clean install doesn't generate plugin.xml
When used in a dependent project, I'm getting the following error
Failed to parse plugin descriptor for it.gruppopam.common:end-point-test-coverage:1 (/home/d/.m2/repository/it/common/end-point-test-coverage/1/end-point-test-coverage-1.jar): No plugin descriptor found at META-INF/maven/plugin.xml -> [Help 1]
[ERROR]
First i would try to set the packaging type to maven-plugin instead of the default which is jar. Furthermore i would suggest to use more up-to-date versions of plugins (maven-compiler-plugin: 3.1) and use a more up-to-date version of maven-plugin-api (3.0? but not 2.0).
you have to remember to change it in your pom.xml to:
<packaging>maven-plugin</packaging>
it is by default:
<packaging>jar</packaging>

Maven JBehave : encoding stories UTF8

We managed to create and run tests with internationalized stories using JBehave within eclipse.
Everything went fine.
But when we tried to run them using the maven plug-in, we cannot get rud of the encoding problem (for example, instead of reading "scénario" from the story, it gets "Scénario" : clearly an UTF8 encoding problem).
Does someone have found a way to get JBehave to read the stories in UTF8 using the maven plug-in ?
What we already tried :
adding -Dfile.encoding=UTF-8 option
changing keyword file using UTF8
changing the whole project encoding in ISO => which works but isn't suitable for production part that need to display messages in UTF8
our 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/xsd/maven-4.0.0.xsd">
...
<properties>
<jbehave.version>3.6.5</jbehave.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<resource.encoding>UTF-8</resource.encoding>
</properties>
<build>
<testOutputDirectory>target/classes</testOutputDirectory>
<testResources>
<testResource>
<directory>src/test/resources</directory>
</testResource>
<testResource>
<directory>src/test/story</directory>
</testResource>
</testResources>
<plugins>
...
<plugin>
<artifactId>maven-eclipse-plugin</artifactId>
<version>2.8</version>
<configuration>
<downloadSources>true</downloadSources>
<downloadJavadocs>true</downloadJavadocs>
<additionalBuildcommands>
<buildcommand>com.google.gdt.eclipse.core.webAppProjectValidator</buildcommand>
</additionalBuildcommands>
<additionalProjectnatures>
<projectnature>com.google.gwt.eclipse.core.gwtNature</projectnature>
</additionalProjectnatures>
<classpathContainers>
<classpathContainer>org.eclipse.jdt.launching.JRE_CONTAINER</classpathContainer>
</classpathContainers>
<additionalConfig>
<file>
<name>.settings/org.eclipse.core.resources.prefs</name>
<content>
<![CDATA[eclipse.preferences.version=1
encoding/<project>=UTF-8]]>
</content>
</file>
</additionalConfig>
</configuration>
</plugin>
<plugin>
<groupId>org.jbehave</groupId>
<artifactId>jbehave-maven-plugin</artifactId>
<version>${jbehave.version}</version>
<executions>
<execution>
<id>run-stories-as-embeddables</id>
<phase>test</phase>
<configuration>
<scope>test</scope>
<includes>
<include>**/*Story.java</include>
</includes>
<ignoreFailureInStories>true</ignoreFailureInStories>
<ignoreFailureInView>false</ignoreFailureInView>
</configuration>
<goals>
<goal>run-stories-as-embeddables</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>unpack-jbehave-site-resources</id>
<phase>generate-resources</phase>
<goals>
<goal>unpack</goal>
</goals>
<configuration>
<overwriteReleases>false</overwriteReleases>
<overwriteSnapshots>true</overwriteSnapshots>
<artifactItems>
<artifactItem>
<groupId>org.jbehave.site</groupId>
<artifactId>jbehave-site-resources</artifactId>
<version>3.1.1</version>
<type>zip</type>
<outputDirectory>
${project.build.directory}/jbehave/view
</outputDirectory>
</artifactItem>
</artifactItems>
</configuration>
</execution>
<execution>
<id>unpack-jbehave-reports-resources</id>
<phase>generate-resources</phase>
<goals>
<goal>unpack</goal>
</goals>
<configuration>
<overwriteReleases>false</overwriteReleases>
<overwriteSnapshots>true</overwriteSnapshots>
<artifactItems>
<artifactItem>
<groupId>org.jbehave</groupId>
<artifactId>jbehave-core</artifactId>
<version>${jbehave.version}</version>
<outputDirectory>
${project.build.directory}/jbehave/view
</outputDirectory>
<includes>
**\/*.css,**\/*.ftl,**\/*.js
</includes>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
...
<!-- JBehave Dependencies -->
<dependency>
<groupId>org.jbehave</groupId>
<artifactId>jbehave-core</artifactId>
<version>${jbehave.version}</version>
</dependency>
<!-- Test Frameworks Dependencies -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.8.4</version>
<scope>test</scope>
</dependency>
</dependencies>
I have had some success subclassing the org.jbehave.core.io.LoadFromClasspath class, which I use in my configuration as the story loader, i.e.
MostUsefulConfiguration().useStoryLoader(new LoadFromClasspathUtf8());
here's my subclass with the proper method override:
import java.io.IOException;
import java.io.InputStream;
import org.apache.commons.io.IOUtils;
import org.jbehave.core.io.InvalidStoryResource;
import org.jbehave.core.io.LoadFromClasspath;
public class LoadFromClasspathUtf8 extends LoadFromClasspath {
public LoadFromClasspathUtf8(Class<?> loadFromClass) {
super(loadFromClass);
}
public LoadFromClasspathUtf8(ClassLoader classLoader) {
super(classLoader);
}
#Override
public String loadResourceAsText(String resourcePath) {
InputStream stream = resourceAsStream(resourcePath);
try {
return IOUtils.toString(stream, "UTF-8");
} catch (IOException e) {
throw new InvalidStoryResource(resourcePath, stream, e);
}
}
}
I say "I had some success" because when I look at the logs of my jbehave execution, accented french characters like è,à,é etc. are replaced by ?, but then, jbehave still matches this correctly to the steps using the regular RegexStoryParser. I didn't take time to investigate why this is, but I'm satisfied that my stories work correctly now.
I also added the file.encoding system property to my plugin configuration to make it clear that I intend to use UTF-8 encoding.
Same problem here as well. Even after adding the "-Dfile.encoding=UTF-8" parameter to MAVEN_OPTS the problem persisted. Turn out that I had this line inside my ~/.mavenrc file:
export MAVEN_OPTS="-Xmx1024m"
What was happening is the MAVEN_OPTS variable got reset before executing the JVM.
After change the ~/.mavenrc file to:
export MAVEN_OPTS="$MAVEN_OPTS -Xmx1024m"
The problem was solved. The file encoding is set correct when running:
export MAVEN_OPTS="$MAVEN_OPTS -Dfile.encoding=UTF-8"
mvn clean integration-test
Since you have your stories in the "test" context rather than "main" (in another module) - I think that there is probably something going on when stories are copied to target/test-classes.
I had exactly the same problem. By default, JBehave doesn't honor platform encoding. In order to fix this, you can use this custom StoryLoader which honors file.encoding system property:
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import org.apache.commons.io.IOUtils;
import org.jbehave.core.io.InvalidStoryResource;
import org.jbehave.core.io.LoadFromClasspath;
/**
* #author cedric.vidal
*
*/
public class FixedStoryLoader extends LoadFromClasspath {
public String loadResourceAsText(String resourcePath) {
InputStream stream = resourceAsStream(resourcePath);
try {
return IOUtils.toString(stream, platformCharset().name());
} catch (IOException e) {
throw new InvalidStoryResource(resourcePath, stream, e);
}
}
public static Charset platformCharset() {
String csn = System.getProperty("file.encoding");
Charset cs = Charset.forName(csn);
if (cs == null) {
cs = Charset.forName("UTF-8");
}
return cs;
}
}
Register it in JBehave configuration with:
new MostUsefulConfiguration().useStoryLoader(new FixedStoryLoader());
Configure your POM to use UTF-8 in all respectfull plugins (will be used by m2eclipse too):
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
And tell the JBehave Maven Plugin to use it also (look for the systemProperties block):
<plugin>
<groupId>org.jbehave</groupId>
<artifactId>jbehave-maven-plugin</artifactId>
<version>${jbehave.core.version}</version>
<executions>
<execution>
<id>unpack-view-resources</id>
<phase>process-resources</phase>
<goals>
<goal>unpack-view-resources</goal>
</goals>
</execution>
<execution>
<id>embeddable-stories</id>
<phase>integration-test</phase>
<configuration>
<includes>
<include>${embeddables}</include>
</includes>
<excludes/>
<systemProperties>
<property>
<name>file.encoding</name>
<value>${project.build.sourceEncoding}</value>
</property>
</systemProperties>
<ignoreFailureInStories>true</ignoreFailureInStories>
<ignoreFailureInView>false</ignoreFailureInView>
<threads>1</threads>
<metaFilters>
<metaFilter/>
</metaFilters>
</configuration>
<goals>
<goal>run-stories-as-embeddables</goal>
</goals>
</execution>
</executions>
</plugin>

Not able to integrate AspectJ with Maven

I banged my head for two days to integrate aspectj with maven, But did not work.
I am writing a simple program with two advices (which works fine in eclipse, so I am sure it's not a code side error).
I have recently started using maven for project building. Can't think of why I am not able to kick off aspectj compiler.
During compilation through maven - I get only class file for java without weaving. .aj file is not compiled.
Please Help!!!!
the first aspect file - ExceptionAspects.aj
package com.aspectSample;
public aspect ExceptionAspects {
pointcut ExceptionHandler(Exception e) : handler(Exception+) && args(e) && within(com.clickable.*);
pointcut callbeforeMethod():execution( public void HelloWorldExclude.*(..)) ;
before(Exception ex) : ExceptionHandler(ex)
{
System.out.println("Added Aspects before Exception :"+ex.toString());
}
before(): callbeforeMethod()
{
System.out.println("Aspect Before Method Call");
}
pointcut sysOutOrErrAccess() : get(* System.out) || get(* System.err);
declare error
: sysOutOrErrAccess()
: "Don't write to the console";
}
The source java file HelloWorldExclude.java
package com.aspectSample;
import java.sql.SQLException;
public class HelloWorldExclude {
private void FoulIntro(String message, String name) throws SQLException
{
throw new SQLException("WriteSomething");
}
public void GiveMeFoulIntro(String message, String name)
{
try
{
this.FoulIntro(message, name);
}catch(SQLException exp)
{
System.out.println("catching exception");
}
}
}
and the pom.xml file is
<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.clickable.HelloWorld-Maven</groupId>
<artifactId>HelloWorldAspect-Maven</artifactId>
<version>1.0.0</version>
<name>HelloWorld Aspect</name>
<packaging>jar</packaging>
<description>Hello World Code</description>
<properties>
<java-version>1.6</java-version>
<org.aspectj-version>1.6.11</org.aspectj-version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.4</version>
<executions>
<execution>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
<configuration>
<source>${java-version}</source>
<target>${java-version}</target>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${org.aspectj-version}</version>
</dependency>
</dependencies>
</project>
A couple of things that you can try:
Explicitly provide the dependencies of the maven-aspectj plugin:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.4</version>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>${aspectj.version}</version>
</dependency>
</dependencies>
with the version of aspectjrt and aspectjtools matching the version of aspectj that you are using.
Do you by any chance have any of your sources in the test folder,
with the src/test folder, then to weave the aspects to the test sources you will have to explicitly mention that in the maven-aspectj plugin:
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
There was a stupid mistake in pom file.
aspectj plugin was added under element, which is just a configuration of all plugins. It doesn't tell compiler what to use.
It worked as soon as I removed plugin out of this element.
Thanks
Pankaj

Resources