Environment:
- OS: Ubuntu 20.04 64bit LTS.
- JDK: 11.0.10
- JavaCPP: 1.5.6
- OpenCV: 4.5.3
- Micronaut: 2.5.9
Recently I have a project regarding computer vision. In this project, it uses Micronaut as the main framework. And it also uses OpenCV built with JavaCPP.
I built OpenCV from JavaCPP by myself with below command:
mvn install --projects openblas,opencv -Djavacpp.platform=linux-x86_64
I have read from JavaCPP's FAQ on which it said that JavaCPP supports GraalVM NativeImage.
My code and build.gradle are as below:
build.gradle:
plugins {
id("groovy")
id("com.github.johnrengelman.shadow") version "7.0.0"
id("io.micronaut.application") version "1.5.3"
}
version = "0.1"
group = "com.example"
repositories {
mavenLocal()
mavenCentral()
}
micronaut {
testRuntime("spock2")
processing {
incremental(true)
annotations("com.example.*")
}
}
dependencies {
annotationProcessor("org.projectlombok:lombok")
annotationProcessor("info.picocli:picocli-codegen")
implementation("info.picocli:picocli")
implementation("io.micronaut:micronaut-runtime")
implementation("io.micronaut.picocli:micronaut-picocli")
implementation("javax.annotation:javax.annotation-api")
compileOnly("org.projectlombok:lombok")
runtimeOnly("ch.qos.logback:logback-classic")
compileOnly("org.graalvm.nativeimage:svm")
implementation("io.micronaut:micronaut-validation")
testImplementation("io.micronaut:micronaut-http-client")
implementation('org.bytedeco:opencv:4.5.3-1.5.6-SNAPSHOT')
}
application {
mainClass.set("com.example.NativeJavacppTestCommand")
}
java {
sourceCompatibility = JavaVersion.toVersion("11")
targetCompatibility = JavaVersion.toVersion("11")
}
code:
package com.example;
import io.micronaut.configuration.picocli.PicocliRunner;
import io.micronaut.context.ApplicationContext;
import lombok.extern.slf4j.Slf4j;
import org.bytedeco.opencv.opencv_core.Mat;
import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;
import picocli.CommandLine.Parameters;
@Command(name = "native-javacpp-test", description = "...",
mixinStandardHelpOptions = true)
@Slf4j
public class NativeJavacppTestCommand implements Runnable {
@Option(names = {"-v", "--verbose"}, description = "...")
boolean verbose;
public static void main(String[] args) throws Exception {
PicocliRunner.run(NativeJavacppTestCommand.class, args);
}
public void run() {
// business logic here
if (verbose) {
System.out.println("Hi!");
}
Mat mat = new Mat();
log.info("Hello, World");
}
}
Build steps:
sh gradlew shadowJar
sh gradlew nativeImage
When I ran the jar file with below command, it worked fine.
java -Djava.library.path=/opt/javacpp-presets/opencv/target/native/org/bytedeco/opencv/linux-x86_64:/opt/javacpp-presets/openblas/target/native/org/bytedeco/openblas/linux-x86_64 -jar build/libs/native-javacpp-test-0.1-all.jar
When I ran the built native image. Below error happened:
./build/native-image/application
10:40:05.322 [main] INFO i.m.context.env.DefaultEnvironment - Established active environments: [cli]
Warning: Could not create an instance of class org.bytedeco.javacpp.presets.javacpp: java.lang.InstantiationException: Type `org.bytedeco.javacpp.presets.javacpp` can not be instantiated reflectively as it does not have a no-parameter constructor or the no-parameter constructor has not been added explicitly to the native image.
Warning: Could not create an instance of class org.bytedeco.javacpp.presets.javacpp: java.lang.InstantiationException: Type `org.bytedeco.javacpp.presets.javacpp` can not be instantiated reflectively as it does not have a no-parameter constructor or the no-parameter constructor has not been added explicitly to the native image.
Warning: Could not create an instance of class org.bytedeco.javacpp.presets.javacpp: java.lang.InstantiationException: Type `org.bytedeco.javacpp.presets.javacpp` can not be instantiated reflectively as it does not have a no-parameter constructor or the no-parameter constructor has not been added explicitly to the native image.
Warning: Version of org.bytedeco:openblas could not be found.
Warning: Could not create an instance of class org.bytedeco.openblas.presets.openblas_nolapack: java.lang.InstantiationException: Type `org.bytedeco.openblas.presets.openblas_nolapack` can not be instantiated reflectively as it does not have a no-parameter constructor or the no-parameter constructor has not been added explicitly to the native image.
Warning: Could not create an instance of class org.bytedeco.openblas.presets.openblas: java.lang.InstantiationException: Type `org.bytedeco.openblas.presets.openblas` can not be instantiated reflectively as it does not have a no-parameter constructor or the no-parameter constructor has not been added explicitly to the native image.
Warning: Version of org.bytedeco:opencv could not be found.
Warning: Could not create an instance of class org.bytedeco.opencv.presets.opencv_core: java.lang.InstantiationException: Type `org.bytedeco.opencv.presets.opencv_core` can not be instantiated reflectively as it does not have a no-parameter constructor or the no-parameter constructor has not been added explicitly to the native image.
Exception in thread "main" java.lang.NoClassDefFoundError: java.lang.ClassNotFoundException: org.bytedeco.javacpp.presets.javacpp
at org.bytedeco.javacpp.Loader.load(Loader.java:1217)
at org.bytedeco.javacpp.Loader.load(Loader.java:1157)
at org.bytedeco.javacpp.Loader.load(Loader.java:1133)
at org.bytedeco.opencv.opencv_core.AbstractArray.<clinit>(AbstractArray.java:18)
at com.oracle.svm.core.classinitialization.ClassInitializationInfo.invokeClassInitializer(ClassInitializationInfo.java:375)
at com.oracle.svm.core.classinitialization.ClassInitializationInfo.initialize(ClassInitializationInfo.java:295)
at java.lang.Class.ensureInitialized(DynamicHub.java:553)
at com.oracle.svm.core.classinitialization.ClassInitializationInfo.initialize(ClassInitializationInfo.java:260)
at java.lang.Class.ensureInitialized(DynamicHub.java:553)
at com.oracle.svm.core.classinitialization.ClassInitializationInfo.initialize(ClassInitializationInfo.java:260)
at com.example.NativeJavacppTestCommand.run(NativeJavacppTestCommand.java:30)
at picocli.CommandLine.executeUserObject(CommandLine.java:1939)
at picocli.CommandLine.access$1300(CommandLine.java:145)
at picocli.CommandLine$RunLast.executeUserObjectOfLastSubcommandWithSameParent(CommandLine.java:2352)
at picocli.CommandLine$RunLast.handle(CommandLine.java:2346)
at picocli.CommandLine$RunLast.handle(CommandLine.java:2311)
at picocli.CommandLine$AbstractParseResultHandler.execute(CommandLine.java:2179)
at picocli.CommandLine.execute(CommandLine.java:2078)
at io.micronaut.configuration.picocli.PicocliRunner.run(PicocliRunner.java:137)
at io.micronaut.configuration.picocli.PicocliRunner.run(PicocliRunner.java:114)
at com.example.NativeJavacppTestCommand.main(NativeJavacppTestCommand.java:22)
Caused by: java.lang.ClassNotFoundException: org.bytedeco.javacpp.presets.javacpp
at com.oracle.svm.core.hub.ClassForNameSupport.forName(ClassForNameSupport.java:64)
at java.lang.Class.forName(DynamicHub.java:1308)
at org.bytedeco.javacpp.Loader.load(Loader.java:1212)
... 20 more
How to let my code run with native image?