quarkiverse / quarkus-poi

Apache POI is an API to access Microsoft Office files. This extension provides integration with Apache POI
https://poi.apache.org/
Apache License 2.0
12 stars 3 forks source link

giving error 'Could not initialize class' in native image #95

Closed cjbi closed 10 months ago

cjbi commented 10 months ago

in pom.xml I've:

io.quarkiverse.poi:quarkus-poi:2.0.5

when running on native image, an error will occur

2024-01-19 05:44:13,746 ERROR [io.qua.ver.htt.run.QuarkusErrorHandler] (executor-thread-8) HTTP Request to /api/apps/27/items/164:exportData failed, error id: 97bfd904-7986-4988-ac14-bbefb0ae1a60-1: java.lang.NoClassDefFoundError: Could not initialize class java.awt.Toolkit
        at java.desktop@21.0.1/java.awt.Color.<clinit>(Color.java:277)
        at org.apache.poi.hssf.util.HSSFColor$HSSFColorPredefined.<init>(HSSFColor.java:113)
        at org.apache.poi.hssf.util.HSSFColor$HSSFColorPredefined.<clinit>(HSSFColor.java:55)
        at org.apache.poi.hssf.model.InternalWorkbook.createExtendedFormat(InternalWorkbook.java:1385)
        at org.apache.poi.hssf.model.InternalWorkbook.createCellXF(InternalWorkbook.java:871)
        at org.apache.poi.hssf.usermodel.HSSFWorkbook.createCellStyle(HSSFWorkbook.java:1297)
        at org.apache.poi.hssf.usermodel.HSSFWorkbook.createCellStyle(HSSFWorkbook.java:124)
        at tech.wetech.metacode.infrastructure.poi.ExcelDataExporter.title(ExcelDataExporter.java:245)
        at tech.wetech.metacode.infrastructure.poi.ExcelDataExporter.writeSheet(ExcelDataExporter.java:152)
        at tech.wetech.metacode.infrastructure.poi.ExcelDataExporter.exportAppItemData(ExcelDataExporter.java:86)
        at tech.wetech.metacode.application.AppApplicationService.exportAppItemData(AppApplicationService.java:316)
        at tech.wetech.metacode.application.AppApplicationService_Subclass.exportAppItemData$$superforward(Unknown Source)
        at tech.wetech.metacode.application.AppApplicationService_Subclass$$function$$1.apply(Unknown Source)
        at io.quarkus.arc.impl.AroundInvokeInvocationContext.proceed(AroundInvokeInvocationContext.java:73)
        at io.quarkus.arc.impl.AroundInvokeInvocationContext.proceed(AroundInvokeInvocationContext.java:62)
        at io.quarkus.narayana.jta.runtime.interceptor.TransactionalInterceptorBase.invokeInOurTx(TransactionalInterceptorBase.java:136)
        at io.quarkus.narayana.jta.runtime.interceptor.TransactionalInterceptorBase.invokeInOurTx(TransactionalInterceptorBase.java:107)
        at io.quarkus.narayana.jta.runtime.interceptor.TransactionalInterceptorRequired.doIntercept(TransactionalInterceptorRequired.java:38)
        at io.quarkus.narayana.jta.runtime.interceptor.TransactionalInterceptorBase.intercept(TransactionalInterceptorBase.java:61)
        at io.quarkus.narayana.jta.runtime.interceptor.TransactionalInterceptorRequired.intercept(TransactionalInterceptorRequired.java:32)
        at io.quarkus.narayana.jta.runtime.interceptor.TransactionalInterceptorRequired_Bean.intercept(Unknown Source)
        at io.quarkus.arc.impl.InterceptorInvocation.invoke(InterceptorInvocation.java:42)
        at io.quarkus.arc.impl.AroundInvokeInvocationContext.perform(AroundInvokeInvocationContext.java:30)
        at io.quarkus.arc.impl.InvocationContexts.performAroundInvoke(InvocationContexts.java:27)
        at tech.wetech.metacode.application.AppApplicationService_Subclass.exportAppItemData(Unknown Source)
        at tech.wetech.metacode.application.AppApplicationService_ClientProxy.exportAppItemData(Unknown Source)
        at tech.wetech.metacode.api.AppResource.exportAppItemData(AppResource.java:118)
        at tech.wetech.metacode.api.AppResource$quarkusrestinvoker$exportAppItemData_d6a1e4c98c92f1217fa6da146bf63aa114036809.invoke(Unknown Source)
        at org.jboss.resteasy.reactive.server.handlers.InvocationHandler.handle(InvocationHandler.java:29)
        at io.quarkus.resteasy.reactive.server.runtime.QuarkusResteasyReactiveRequestContext.invokeHandler(QuarkusResteasyReactiveRequestContext.java:141)
        at org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:147)
        at io.quarkus.vertx.core.runtime.VertxCoreRecorder$14.runWith(VertxCoreRecorder.java:582)
        at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2513)
        at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1538)
        at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29)
        at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29)
        at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
        at java.base@21.0.1/java.lang.Thread.runWith(Thread.java:1596)
        at java.base@21.0.1/java.lang.Thread.run(Thread.java:1583)
        at org.graalvm.nativeimage.builder/com.oracle.svm.core.thread.PlatformThreads.threadStartRoutine(PlatformThreads.java:832)
        at org.graalvm.nativeimage.builder/com.oracle.svm.core.posix.thread.PosixPlatformThreads.pthreadStartRoutine(PosixPlatformThreads.java:211)

2024-01-19 05:44:25,605 ERROR [io.qua.ver.htt.run.QuarkusErrorHandler] (executor-thread-8) HTTP Request to /api/apps/27/items/164:exportData failed, error id: 97bfd904-7986-4988-ac14-bbefb0ae1a60-2: java.lang.NoClassDefFoundError: Could not initialize class org.apache.poi.hssf.util.HSSFColor$HSSFColorPredefined
        at org.apache.poi.hssf.model.InternalWorkbook.createExtendedFormat(InternalWorkbook.java:1385)
        at org.apache.poi.hssf.model.InternalWorkbook.createCellXF(InternalWorkbook.java:871)
        at org.apache.poi.hssf.usermodel.HSSFWorkbook.createCellStyle(HSSFWorkbook.java:1297)
        at org.apache.poi.hssf.usermodel.HSSFWorkbook.createCellStyle(HSSFWorkbook.java:124)
        at tech.wetech.metacode.infrastructure.poi.ExcelDataExporter.title(ExcelDataExporter.java:245)
        at tech.wetech.metacode.infrastructure.poi.ExcelDataExporter.writeSheet(ExcelDataExporter.java:152)
        at tech.wetech.metacode.infrastructure.poi.ExcelDataExporter.exportAppItemData(ExcelDataExporter.java:86)
        at tech.wetech.metacode.application.AppApplicationService.exportAppItemData(AppApplicationService.java:316)
        at tech.wetech.metacode.application.AppApplicationService_Subclass.exportAppItemData$$superforward(Unknown Source)
        at tech.wetech.metacode.application.AppApplicationService_Subclass$$function$$1.apply(Unknown Source)
        at io.quarkus.arc.impl.AroundInvokeInvocationContext.proceed(AroundInvokeInvocationContext.java:73)
        at io.quarkus.arc.impl.AroundInvokeInvocationContext.proceed(AroundInvokeInvocationContext.java:62)
        at io.quarkus.narayana.jta.runtime.interceptor.TransactionalInterceptorBase.invokeInOurTx(TransactionalInterceptorBase.java:136)
        at io.quarkus.narayana.jta.runtime.interceptor.TransactionalInterceptorBase.invokeInOurTx(TransactionalInterceptorBase.java:107)
        at io.quarkus.narayana.jta.runtime.interceptor.TransactionalInterceptorRequired.doIntercept(TransactionalInterceptorRequired.java:38)
        at io.quarkus.narayana.jta.runtime.interceptor.TransactionalInterceptorBase.intercept(TransactionalInterceptorBase.java:61)
        at io.quarkus.narayana.jta.runtime.interceptor.TransactionalInterceptorRequired.intercept(TransactionalInterceptorRequired.java:32)
        at io.quarkus.narayana.jta.runtime.interceptor.TransactionalInterceptorRequired_Bean.intercept(Unknown Source)
        at io.quarkus.arc.impl.InterceptorInvocation.invoke(InterceptorInvocation.java:42)
        at io.quarkus.arc.impl.AroundInvokeInvocationContext.perform(AroundInvokeInvocationContext.java:30)
        at io.quarkus.arc.impl.InvocationContexts.performAroundInvoke(InvocationContexts.java:27)
        at tech.wetech.metacode.application.AppApplicationService_Subclass.exportAppItemData(Unknown Source)
        at tech.wetech.metacode.application.AppApplicationService_ClientProxy.exportAppItemData(Unknown Source)
        at tech.wetech.metacode.api.AppResource.exportAppItemData(AppResource.java:118)
        at tech.wetech.metacode.api.AppResource$quarkusrestinvoker$exportAppItemData_d6a1e4c98c92f1217fa6da146bf63aa114036809.invoke(Unknown Source)
        at org.jboss.resteasy.reactive.server.handlers.InvocationHandler.handle(InvocationHandler.java:29)
        at io.quarkus.resteasy.reactive.server.runtime.QuarkusResteasyReactiveRequestContext.invokeHandler(QuarkusResteasyReactiveRequestContext.java:141)
        at org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:147)
        at io.quarkus.vertx.core.runtime.VertxCoreRecorder$14.runWith(VertxCoreRecorder.java:582)
        at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2513)
        at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1538)
        at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29)
        at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29)
        at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
        at java.base@21.0.1/java.lang.Thread.runWith(Thread.java:1596)
        at java.base@21.0.1/java.lang.Thread.run(Thread.java:1583)
        at org.graalvm.nativeimage.builder/com.oracle.svm.core.thread.PlatformThreads.threadStartRoutine(PlatformThreads.java:832)
        at org.graalvm.nativeimage.builder/com.oracle.svm.core.posix.thread.PosixPlatformThreads.pthreadStartRoutine(PosixPlatformThreads.java:211)

when I'm running my project without native image it is running fine.

Output of java -version 21

Quarkus version or git rev 3.6.3

Build tool (ie. output of mvnw --version or gradlew --version) Apache Maven 3.9.5

melloware commented 10 months ago

Can you provide some example poi code on how you get this error? Keeping it as simple as possible?

cjbi commented 10 months ago

Can you provide some example poi code on how you get this error? Keeping it as simple as possible?

The sample code:

https://github.com/cjbi/quarkus-poi-bug-report

Gitpod online demo:

https://gitpod.io/new#https://github.com/cjbi/quarkus-poi-bug-report

gastaldi commented 10 months ago

@cjbi can you check if adding the following dependency works for you?

<dependency>
   <groupId>io.quarkus</groupId>
   <artifactId>quarkus-awt</artifactId>
</dependency>
cjbi commented 10 months ago

@cjbi can you check if adding the following dependency works for you?

<dependency>
   <groupId>io.quarkus</groupId>
   <artifactId>quarkus-awt</artifactId>
</dependency>

Thank you for your answer. However, Adding this dependency to my project, it still doesn't seem to be working.

gastaldi commented 10 months ago

Sounds like you need to add that font to your native distribution, see https://quarkus.io/guides/writing-native-applications-tips#including-resources

melloware commented 10 months ago

agreed with @gastaldi your native image can't find the Font you are trying to use.

Font font = workbook.createFont();
font.setFontName("黑体");

You need to include that font.

Also note this issue with fonts: https://github.com/quarkiverse/quarkus-poi?tab=readme-ov-file#docker

cjbi commented 10 months ago

Thanks @gastaldi ,I removed the font, but it still didn't work. However,I found that when removed workbook.createCellStyle(),it is running fine in native image, the code fragment :

 while (cellNo < cellLength) {
                Cell cell = headRow.createCell(cellNo++);
            //    CellStyle style = workbook.createCellStyle();
            //  cell.setCellStyle(style);
                cell.setCellValue("test name" + cellNo);
 }

Please look at the two lines of code with comments, when remove comments, it will cause an error..

melloware commented 10 months ago

Running my integration test now with that change

melloware commented 10 months ago

Worked on Windows and Linux and ONLY fails on MacOS.

image

cjbi commented 10 months ago

Worked on Windows and Linux and ONLY fails on MacOS.

image

i don't used macos,I found github actions can also reproduce: https://github.com/cjbi/quarkus-poi-bug-report/actions/runs/7663068194

melloware commented 10 months ago

@cjbi if you are using Red Hat ubi-minimal did you see this note: https://github.com/quarkiverse/quarkus-poi?tab=readme-ov-file#docker

It won't work in ubi-minimal without that script. For example here is one of mine that uses POI Native in UbiMinimal: https://github.com/melloware/quarkus-faces/blob/main/src/main/docker/Dockerfile.native

cjbi commented 10 months ago

I'm sorry bro @melloware , i don't notice the readme, but it still doesn't work after adding.

gastaldi commented 10 months ago

I wonder if it’s a bug in Mandrel. Can you change to GraalVM CE just to check?

gastaldi commented 10 months ago

@zakkak any hints?

cjbi commented 10 months ago

@gastaldi I using graalvm-ce from https://www.graalvm.org/latest/docs/getting-started/container-images/, it also can’t working.

zakkak commented 10 months ago

Trying this without -Dquarkus.container-image.build=true seems to work fine on linux. So it looks like a packaging issue.

Investigating further one can see that when building that native image a bunch of library files are placed next to the application:

❯ ls ./target/
classes/            libjavajpeg.so    quarkus-app/
generated-sources/  libjava.so*       quarkus-artifact.properties
libawt_headless.so  libjvm.so*        quarkus-poi-bug-report-1.0.0-SNAPSHOT.jar
libawt.so           liblcms.so        quarkus-poi-bug-report-1.0.0-SNAPSHOT-native-image-source-jar/
libawt_xawt.so      libmlib_image.so  quarkus-poi-bug-report-1.0.0-SNAPSHOT-runner*
libfontmanager.so   maven-archiver/
libfreetype.so      maven-status/

Now if we move the binary to a different location, e.g. /tmp, we will get the same error you get in the docker image:

❯ /tmp/quarkus-poi-bug-report-1.0.0-SNAPSHOT-runner 
__  ____  __  _____   ___  __ ____  ______ 
 --/ __ \/ / / / _ | / _ \/ //_/ / / / __/ 
 -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \   
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/   
2024-01-26 11:20:24,363 INFO  [io.quarkus] (main) quarkus-poi-bug-report 1.0.0-SNAPSHOT native (powered by Quarkus 3.6.6) started in 0.003s. 
2024-01-26 11:20:24,363 INFO  [io.quarkus] (main) Profile prod activated. 
2024-01-26 11:20:24,363 INFO  [io.quarkus] (main) Installed features: [awt, cdi, poi, smallrye-context-propagation]
2024-01-26 11:20:24,363 INFO  [ListenerBean] (main) ------------Application started
Exception in thread "main" java.lang.NoClassDefFoundError: Could not initialize class java.awt.Toolkit
    at java.desktop@21.0.2/java.awt.Color.<clinit>(Color.java:277)
    at org.apache.poi.hssf.util.HSSFColor$HSSFColorPredefined.<init>(HSSFColor.java:113)
    at org.apache.poi.hssf.util.HSSFColor$HSSFColorPredefined.<clinit>(HSSFColor.java:55)
    at org.apache.poi.hssf.model.InternalWorkbook.createExtendedFormat(InternalWorkbook.java:1406)
    at org.apache.poi.hssf.model.InternalWorkbook.createCellXF(InternalWorkbook.java:892)
    at org.apache.poi.hssf.usermodel.HSSFWorkbook.createCellStyle(HSSFWorkbook.java:1303)
    at org.apache.poi.hssf.usermodel.HSSFWorkbook.createCellStyle(HSSFWorkbook.java:126)
    at Main$ExcelDataExporter.run(Main.java:56)
    at Main_ExcelDataExporter_ClientProxy.run(Unknown Source)
    at io.quarkus.runtime.ApplicationLifecycleManager.run(ApplicationLifecycleManager.java:132)
    at io.quarkus.runtime.Quarkus.run(Quarkus.java:71)
    at io.quarkus.runtime.Quarkus.run(Quarkus.java:44)
    at Main.main(Main.java:24)

So what happens here is that the application depends on those library files, but the libraries are not transferred to the docker image. This looks like a known issue https://github.com/quarkusio/quarkus/pull/32576#issuecomment-1505737260 that slept through the cracks and never got fixed (cc @karm).

Patching the reproducer with:

diff --git a/.dockerignore b/.dockerignore
index 94810d0..c8324b0 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -1,5 +1,6 @@
 *
 !target/*-runner
 !target/*-runner.jar
+!target/*.so
 !target/lib/*
 !target/quarkus-app/*
\ No newline at end of file
diff --git a/src/main/docker/Dockerfile.native b/src/main/docker/Dockerfile.native
index 8269f4f..45823b2 100644
--- a/src/main/docker/Dockerfile.native
+++ b/src/main/docker/Dockerfile.native
@@ -22,6 +22,8 @@ WORKDIR /work/
 RUN chown 1001 /work \
     && chmod "g+rwX" /work \
     && chown 1001:root /work
+# Shared objects to be dynamically loaded at runtime as needed
+COPY --chown=1001:root target/*.so /work/
 COPY --chown=1001:root target/*-runner /work/application

 EXPOSE 8080

works-around the issue.

❯ podman run cjbi/quarkus-poi-test                  
__  ____  __  _____   ___  __ ____  ______ 
 --/ __ \/ / / / _ | / _ \/ //_/ / / / __/ 
 -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \   
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/   
2024-01-26 09:27:41,204 WARN  [io.qua.config] (main) Unrecognized configuration key "quarkus.http.host" was provided; it will be ignored; verify that the dependency extension for this configuration is set or that you did not make a typo
2024-01-26 09:27:41,204 INFO  [io.quarkus] (main) quarkus-poi-bug-report 1.0.0-SNAPSHOT native (powered by Quarkus 3.6.6) started in 0.003s. 
2024-01-26 09:27:41,204 INFO  [io.quarkus] (main) Profile prod activated. 
2024-01-26 09:27:41,204 INFO  [io.quarkus] (main) Installed features: [awt, cdi, poi, smallrye-context-propagation]
2024-01-26 09:27:41,204 INFO  [ListenerBean] (main) ------------Application started
2024-01-26 09:27:41,216 INFO  [ListenerBean] (main) ------------Temp file path:/tmp/test_20240126092741.xls
2024-01-26 09:27:41,216 WARN  [org.apa.poi.POIDocument] (main) DocumentSummaryInformation property set came back as null
2024-01-26 09:27:41,216 WARN  [org.apa.poi.POIDocument] (main) SummaryInformation property set came back as null
2024-01-26 09:27:41,217 INFO  [ListenerBean] (main) ------------Output Successful

Thanks for bringing this to our attention!

cjbi commented 10 months ago

@zakkak Thanks for your help, i modified my code it's working fine.

gastaldi commented 10 months ago

Thank you for the help @zakkak ! I'm going to close this now

zakkak commented 10 months ago

For the record, the upstream issue tracking this is https://github.com/quarkusio/quarkus/issues/38412