Skip to content

Commit

Permalink
Avoid classes with incomplete hierarchy in Hibernate Validator
Browse files Browse the repository at this point in the history
As explained in quarkiverse/quarkus-poi#102, some classes cannot be loaded because of an incomplete hierarchy
  • Loading branch information
gastaldi committed Apr 30, 2024
1 parent 01c11a0 commit ade302c
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -562,10 +562,7 @@ public void build(
jaxRsMethods,
methodsWithInheritedValidation)));

Set<Class<?>> classesToBeValidated = new HashSet<>();
for (DotName className : classNamesToBeValidated) {
classesToBeValidated.add(recorderContext.classProxy(className.toString()));
}
Set<Class<?>> classesToBeValidated = toClassSet(classNamesToBeValidated);

// Prevent the removal of ValueExtractor beans
// and collect all classes implementing ValueExtractor (for use in HibernateValidatorRecorder)
Expand All @@ -574,10 +571,7 @@ public void build(
valueExtractorClassNames.add(valueExtractorType.name());
}
unremovableBeans.produce(UnremovableBeanBuildItem.beanTypes(valueExtractorClassNames));
Set<Class<?>> valueExtractorClassProxies = new HashSet<>();
for (DotName className : valueExtractorClassNames) {
valueExtractorClassProxies.add(recorderContext.classProxy(className.toString()));
}
Set<Class<?>> valueExtractorClassProxies = toClassSet(valueExtractorClassNames);

beanContainerListener
.produce(new BeanContainerListenerBuildItem(
Expand All @@ -590,6 +584,21 @@ public void build(
hibernateValidatorBuildTimeConfig)));
}

private static Set<Class<?>> toClassSet(Set<DotName> dotNameSet) {
Set<Class<?>> classSet = new HashSet<>(dotNameSet.size());
for (DotName className : dotNameSet) {
try {
Class<?> type = Class.forName(className.toString(), true, Thread.currentThread().getContextClassLoader());
// We call getCanonicalName() to make sure the class hierarchy can be loaded
LOG.debugf("Loading %s", type.getCanonicalName());
classSet.add(type);
} catch (NoClassDefFoundError | ClassNotFoundException e) {
LOG.debugf(e, "Unable to load class %s", className);
}
}
return classSet;
}

@BuildStep
void indexAdditionalConstrainedClasses(List<AdditionalConstrainedClassBuildItem> additionalConstrainedClasses,
BuildProducer<AdditionalConstrainedClassesIndexBuildItem> additionalConstrainedClassesIndex) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package io.quarkus.hibernate.validator.test;

import static org.assertj.core.api.Assertions.assertThat;

import java.util.AbstractCollection;

import jakarta.inject.Inject;
import jakarta.validation.Valid;
import jakarta.validation.Validator;

import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.ByteArrayAsset;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.test.QuarkusUnitTest;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType;

public class ClassHierarchyTest {

@RegisterExtension
static final QuarkusUnitTest test = new QuarkusUnitTest().setArchiveProducer(() -> ShrinkWrap
.create(JavaArchive.class)
.add(new ByteArrayAsset(createFooClass()), "InnerClass.class")
.addClass(Dto.class));

private static byte[] createFooClass() {
// Create the outer class
try (DynamicType.Unloaded<?> superClass = new ByteBuddy()
.subclass(Object.class)
.name("SuperClass")
.make();
DynamicType.Unloaded<?> outerClass = new ByteBuddy()
.subclass(superClass.getTypeDescription())
.name("OuterClass")
.make();
DynamicType.Unloaded<?> innerClass = new ByteBuddy()
.subclass(AbstractCollection.class)
.innerTypeOf(outerClass.getTypeDescription())
.name("InnerClass")
.make();
DynamicType.Loaded<?> load = innerClass.load(Thread.currentThread().getContextClassLoader())) {
return load.getBytes();
}
}

@Inject
Validator validator;

@Test
public void doNotFailWhenLoadingIncompleteClassHierarchy() {
assertThat(validator).isNotNull();
}

@Valid
public static class Dto {
String name;

// XMLBean.ErrorLogger is a subclass with an incomplete hierarchy
@Valid
AbstractCollection<String> items;
}
}

0 comments on commit ade302c

Please sign in to comment.