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

Bug: unwanted build-time third-party classes on classpath cause failure with bean validation supported by Hibernate validator. #102

Closed vondacho closed 6 months ago

vondacho commented 8 months ago

Concern

The quarkus-poi extension transitively relies on org.apache.xmlbeans:xmlbeans dependency which exposes runtime and build-time classes. This can cause failures at build-time, or in devmode when reloading, with an application that uses the quarkus-poi extension and does bean validation using the hibernate-validation extension.

More generally, this problem can happen with any constellation which includes the hibernate-validation extension, one Quarkus extension that depends on the org.apache.xmlbeans:xmlbeans dependency, and one DTO class to be validated that has a connection with the AbstractCollection class.

More details

The HibernateValidatorProcessor class recursively scans every class to be validated including their fields to be validated, their subclasses and superclass. Once it recursively meets the AbstractCollection class, it scans every specialization, consequently, it can recursively meets classes from org.apache.xmlbeans, for example the XMLBean$ErrorLogger one, which parent, the XMLBean class, extends the org.apache.tools.ant.taskdefs.MatchingTask build-time class. This latter is not per default on the classpath and then, the Quarkus application fails at build-time and on reload in devmode.

Reproducer

  1. To create an empty Quarkus application.
  2. To declare both quarkus-poi and hibernate-validation extensions in the pom.xml.
  3. To define a DTO class to be validated and that defines a field to be validated of type AbstractCollection.
  4. To annotate both the DTO class and its field with the @Valid annotation.
@Valid
public class Dto {
    String name;
    @Valid
    AbstractCollection<String> items;
}
<dependencies>
    <dependency>
        <groupId>io.quarkus</groupId>
        <artifactId>quarkus-arc</artifactId>
    </dependency>
    <dependency>
        <groupId>io.quarkus</groupId>
        <artifactId>quarkus-hibernate-validator</artifactId>
    </dependency>
    <dependency>
        <groupId>io.quarkiverse.poi</groupId>
        <artifactId>quarkus-poi</artifactId>
        <version>2.0.5</version>
    </dependency>
</dependencies>

Variants

gastaldi commented 8 months ago

@gsmet @yrodiere is this something that needs to be improved in the Hibernate Validator extension?

gastaldi commented 8 months ago

Here is the full stacktrace for record purposes (running in Quarkus 3.8.3):

2024-03-20 10:45:57,594 ERROR [io.qua.run.boo.StartupActionImpl] (Quarkus Main Thread) Error running Quarkus: java.lang.reflect.InvocationTargetException
    at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:118)
    at java.base/java.lang.reflect.Method.invoke(Method.java:580)
    at io.quarkus.runner.bootstrap.StartupActionImpl$1.run(StartupActionImpl.java:113)
    at java.base/java.lang.Thread.run(Thread.java:1583)
Caused by: java.lang.ExceptionInInitializerError
    at java.base/jdk.internal.misc.Unsafe.ensureClassInitialized0(Native Method)
    at java.base/jdk.internal.misc.Unsafe.ensureClassInitialized(Unsafe.java:1160)
    at java.base/jdk.internal.reflect.MethodHandleAccessorFactory.ensureClassInitialized(MethodHandleAccessorFactory.java:300)
    at java.base/jdk.internal.reflect.MethodHandleAccessorFactory.newConstructorAccessor(MethodHandleAccessorFactory.java:103)
    at java.base/jdk.internal.reflect.ReflectionFactory.newConstructorAccessor(ReflectionFactory.java:200)
    at java.base/java.lang.reflect.Constructor.acquireConstructorAccessor(Constructor.java:549)
    at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499)
    at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:486)
    at io.quarkus.runtime.Quarkus.run(Quarkus.java:70)
    at io.quarkus.runtime.Quarkus.run(Quarkus.java:44)
    at io.quarkus.runtime.Quarkus.run(Quarkus.java:124)
    at io.quarkus.runner.GeneratedMain.main(Unknown Source)
    at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
    ... 3 more
Caused by: java.lang.RuntimeException: Failed to start quarkus
    at io.quarkus.runner.ApplicationImpl.<clinit>(Unknown Source)
    ... 16 more
Caused by: java.lang.NoClassDefFoundError: org/apache/tools/ant/taskdefs/MatchingTask
    at java.base/java.lang.ClassLoader.defineClass1(Native Method)
    at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1027)
    at io.quarkus.bootstrap.classloading.QuarkusClassLoader.loadClass(QuarkusClassLoader.java:508)
    at io.quarkus.bootstrap.classloading.QuarkusClassLoader.loadClass(QuarkusClassLoader.java:468)
    at java.base/java.lang.Class.getDeclaredFields0(Native Method)
    at java.base/java.lang.Class.privateGetDeclaredFields(Class.java:3473)
    at java.base/java.lang.Class.getDeclaredFields(Class.java:2542)
    at org.hibernate.validator.internal.util.privilegedactions.GetDeclaredFields.run(GetDeclaredFields.java:30)
    at org.hibernate.validator.internal.util.privilegedactions.GetDeclaredFields.run(GetDeclaredFields.java:17)
    at org.hibernate.validator.internal.metadata.provider.AnnotationMetaDataProvider.run(AnnotationMetaDataProvider.java:602)
    at org.hibernate.validator.internal.metadata.provider.AnnotationMetaDataProvider.getFieldMetaData(AnnotationMetaDataProvider.java:217)
    at org.hibernate.validator.internal.metadata.provider.AnnotationMetaDataProvider.retrieveBeanConfiguration(AnnotationMetaDataProvider.java:130)
    at org.hibernate.validator.internal.metadata.provider.AnnotationMetaDataProvider.getBeanConfiguration(AnnotationMetaDataProvider.java:121)
    at org.hibernate.validator.internal.metadata.PredefinedScopeBeanMetaDataManager.getBeanConfigurationForHierarchy(PredefinedScopeBeanMetaDataManager.java:183)
    at org.hibernate.validator.internal.metadata.PredefinedScopeBeanMetaDataManager.createBeanMetaData(PredefinedScopeBeanMetaDataManager.java:150)
    at org.hibernate.validator.internal.metadata.PredefinedScopeBeanMetaDataManager.<init>(PredefinedScopeBeanMetaDataManager.java:100)
    at org.hibernate.validator.internal.engine.PredefinedScopeValidatorFactoryImpl.<init>(PredefinedScopeValidatorFactoryImpl.java:206)
    at org.hibernate.validator.PredefinedScopeHibernateValidator.buildValidatorFactory(PredefinedScopeHibernateValidator.java:42)
    at org.hibernate.validator.internal.engine.AbstractConfigurationImpl.buildValidatorFactory(AbstractConfigurationImpl.java:435)
    at io.quarkus.hibernate.validator.runtime.HibernateValidatorRecorder$2.created(HibernateValidatorRecorder.java:180)
    at io.quarkus.arc.runtime.ArcRecorder.initBeanContainer(ArcRecorder.java:79)
    at io.quarkus.deployment.steps.ArcProcessor$notifyBeanContainerListeners1304312071.deploy_0(Unknown Source)
    at io.quarkus.deployment.steps.ArcProcessor$notifyBeanContainerListeners1304312071.deploy(Unknown Source)
    ... 17 more
Caused by: java.lang.ClassNotFoundException: org.apache.tools.ant.taskdefs.MatchingTask
    at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
    at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526)
    at io.quarkus.bootstrap.classloading.QuarkusClassLoader.loadClass(QuarkusClassLoader.java:518)
    at io.quarkus.bootstrap.classloading.QuarkusClassLoader.loadClass(QuarkusClassLoader.java:468)
    ... 40 more
vsevel commented 8 months ago

note @aloubyansky that this issue is part of the bigger story of us trying to chase the different problems associated with dev mode. apart from this one, and a few difficult to reproduce classloading issues, everything has been solved to date. we will continue testing our apps with dev mode, as we are convinced this is a major strength point for quarkus.

aloubyansky commented 8 months ago

Wow, thanks for the info!

yrodiere commented 7 months ago

Hey @melloware , can you please explain why this was closed as "not planned" -- for the record?

vsevel commented 7 months ago

and what would be the proposed solution? thanks

melloware commented 7 months ago

I thought this was resolved as NOT a POI issue. I am using POI with Hibernate Validator without issue so this is strange. I have re-opened it.

vsevel commented 7 months ago

have you tried the reproducer? does it match your situation?

melloware commented 7 months ago

@vsevel yep i think the difference is I am not using @Valid on a collection

gastaldi commented 6 months ago

I've created a PR fixing this in https://github.com/quarkusio/quarkus/pull/40383, can someone please give it a try?

gastaldi commented 6 months ago

I've merged the fix in Quarkus core: https://github.com/quarkusio/quarkus/commit/09685855ecc27e0537cac71c0f46c3384b7770a4

It is marked to be backported, so let's close this issue when the next release containing the fix is available

gastaldi commented 6 months ago

Quarkus 3.10.1 containing the fix is now available. Closing this issue now

melloware commented 6 months ago

Nice!