qr-codequarkuszxingquarkus-native

Quarkus 3 QR generation using google.zxing native build error


I am creating a Quarkus 3 application to create a QR code given an url. In non-native build it works fine, but when packaging it in native mode, it gives me an error about some missing path.

Caused by: java.lang.UnsatisfiedLinkError: no awt in java.library.path at org.graalvm.nativeimage.builder/com.oracle.svm.core.jdk.NativeLibrarySupport.loadLibraryRelative(NativeLibrarySupport.java:136) at java.base@17.0.8/java.lang.ClassLoader.loadLibrary(ClassLoader.java:50)

Here's the pom.xml:

<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-awt</artifactId>
</dependency>
<dependency>
    <groupId>com.google.zxing</groupId>
    <artifactId>core</artifactId>
    <version>3.5.2</version>
</dependency>
<dependency>
    <groupId>com.google.zxing</groupId>
    <artifactId>javase</artifactId>
    <version>3.5.2</version>
</dependency>

Here is the code i am using to generate the QR Code:

public class QRCodeGenerator {

    public static byte[] getQRCodeBytes(String text, int width, int height)
            throws WriterException, IOException {
        String imageFormat = "png"; // could be "gif", "tiff", "jpeg"
        QRCodeWriter qrCodeWriter = new QRCodeWriter();
        BitMatrix bitMatrix = qrCodeWriter.
                encode(text, BarcodeFormat.QR_CODE, width, height);
        ByteArrayOutputStream pngOutputStream =
                new ByteArrayOutputStream();
        MatrixToImageWriter.writeToStream(bitMatrix, imageFormat, pngOutputStream);
        return pngOutputStream.toByteArray();
    }

    public static String getQRCodeBase64(String text, int width, int height)
            throws WriterException, IOException {
        return encodeBase64String(getQRCodeBytes(text, width, height));
    }

    private static String encodeBase64String(byte[] binaryData) {
        return Base64.getEncoder().encodeToString(binaryData);
    }
}

I am asking if anyone has a solution for the error, or another way to handle the QR code generation. Many thanks!

EDIT: Here is the dockerfile (i am using Quarkus default native Dockerfile, with a minor modification):

FROM registry.access.redhat.com/ubi8/ubi-minimal:8.8
WORKDIR /work/

COPY ./build_files/*-runner /work/application

EXPOSE 8080

ENTRYPOINT ["./application", "-Dquarkus.http.host=0.0.0.0"]

Also here is my Jenkins pipeline config to build this project:

mkdir -p /var/xplorer/xplorer-label-dev/build_files
cp target/xplorer-label-*.jar /var/xplorer/xplorer-label-dev/build_files/xplorer-label.jar
cp target/xplorer-label-*-runner /var/xplorer/xplorer-label-dev/build_files/xplorer-label-runner
cp -f Dockerfile /var/xplorer/xplorer-label-dev/Dockerfile
cp -f docker-compose.yml /var/xplorer/xplorer-label-dev/docker-compose.yml

Solution

  • When we execute the native build, we see that some *.so-Files are also listed as artifacts:

    $ ./mvnw --define native clean verify
    ...
    Produced artifacts:
     /project/libawt.so (jdk_library)
     /project/libawt_headless.so (jdk_library)
     /project/libawt_xawt.so (jdk_library)
     /project/libfontmanager.so (jdk_library)
     /project/libjava.so (jdk_library_shim)
     /project/libjavajpeg.so (jdk_library)
     /project/libjvm.so (jdk_library_shim)
     /project/liblcms.so (jdk_library)
     /project/libmlib_image.so (jdk_library)
    ...
    

    These libraries need to be copied to the container as well:

    FROM registry.access.redhat.com/ubi8/ubi-minimal:8.8
    WORKDIR /work/
    
    COPY ./build_files/*.so /work/
    COPY ./build_files/*-runner /work/application
    
    EXPOSE 8080
    
    ENTRYPOINT ["./application", "-Dquarkus.http.host=0.0.0.0"]
    

    I have a very similar application on github.com. This is my corresponding containerfile (github.com):

    ARG DISTROLESS_IMAGE="quay.io/quarkus/quarkus-distroless-image:2.0@sha256:142abd361c081de7e4f7922362eb73e3d98f4ca060178688aeb4c969101e853b"
    
    FROM ${DISTROLESS_IMAGE} as runner
    ARG APP_DIR=/deployment
    ARG UID=1001
    
    USER root
    WORKDIR ${APP_DIR}
    COPY \
      --chmod=444 \
      target/*.so /lib/
    COPY \
      --chmod=111 \
       target/*-runner ${APP_DIR}/application
    
    ENV LANGUAGE='en_US:en'
    USER ${UID}:${UID}
    ENTRYPOINT [ "./application" ]