Skip to content

Commit

Permalink
Avoid classes with incomplete hierarchy in Hibernate Validator
Browse files Browse the repository at this point in the history
  • Loading branch information
gastaldi committed May 1, 2024
1 parent 52b538b commit 93ecb15
Show file tree
Hide file tree
Showing 2 changed files with 86 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,25 @@ public void build(
hibernateValidatorBuildTimeConfig)));
}

private static Set<Class<?>> toClassSet(Set<DotName> dotNames) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Set<Class<?>> classSet = new HashSet<>(dotNames.size());
for (DotName item : dotNames) {
String className = item.toString();
try {
if (QuarkusClassLoader.isClassPresentAtRuntime(className)) {
Class<?> type = classLoader.loadClass(className);
// We call getCanonicalName() to make sure the class hierarchy can be loaded
LOG.debugf("Type %s successfully loaded", 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(createClassWithIncompleteHierarchy()), "InnerClass.class")
.addClass(Dto.class));

private static byte[] createClassWithIncompleteHierarchy() {
// Create an inner class with an incomplete hierarchy
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 93ecb15

Please sign in to comment.