Log and skip resource hint registration for classpath location patterns

Since we do not yet have support for registering resource hints for
classpath location patterns, we have decided to explicitly skip such
resources and log a warning to inform users that they need to manually
supply resource hints for the exact resources needed by their
application.

This commit applies this change for @⁠PropertySource and
@⁠TestPropertySource.

See gh-31162
Closes gh-31429
This commit is contained in:
Sam Brannen
2023-10-25 12:25:46 +02:00
parent 9a7f141867
commit d7e6b79336
4 changed files with 200 additions and 14 deletions

View File

@@ -18,7 +18,10 @@ package org.springframework.test.context.aot;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.stream.Stream;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aot.hint.ResourceHints;
import org.springframework.aot.hint.RuntimeHints;
@@ -31,7 +34,8 @@ import org.springframework.test.context.MergedContextConfiguration;
import org.springframework.util.ClassUtils;
import static org.springframework.aot.hint.MemberCategory.INVOKE_DECLARED_CONSTRUCTORS;
import static org.springframework.util.ResourceUtils.CLASSPATH_URL_PREFIX;
import static org.springframework.core.io.ResourceLoader.CLASSPATH_URL_PREFIX;
import static org.springframework.core.io.support.ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX;
/**
* {@code MergedContextConfigurationRuntimeHints} registers run-time hints for
@@ -57,6 +61,8 @@ class MergedContextConfigurationRuntimeHints {
private static final Method getResourceBasePathMethod = loadGetResourceBasePathMethod();
private final Log logger = LogFactory.getLog(getClass());
@SuppressWarnings("deprecation")
public void registerHints(RuntimeHints runtimeHints, MergedContextConfiguration mergedConfig, ClassLoader classLoader) {
@@ -71,11 +77,11 @@ class MergedContextConfigurationRuntimeHints {
.forEach(clazz -> registerDeclaredConstructors(clazz, runtimeHints));
// @ContextConfiguration(locations = ...)
registerClasspathResources(mergedConfig.getLocations(), runtimeHints, classLoader);
registerClasspathResources("@ContextConfiguration", mergedConfig.getLocations(), runtimeHints, classLoader);
for (PropertySourceDescriptor descriptor : mergedConfig.getPropertySourceDescriptors()) {
// @TestPropertySource(locations = ...)
registerClasspathResources(descriptor.locations().stream(), runtimeHints, classLoader);
registerClasspathResources("@TestPropertySource", descriptor.locations(), runtimeHints, classLoader);
// @TestPropertySource(factory = ...)
Class<?> factoryClass = descriptor.propertySourceFactory();
@@ -102,17 +108,32 @@ class MergedContextConfigurationRuntimeHints {
runtimeHints.reflection().registerType(type, INVOKE_DECLARED_CONSTRUCTORS);
}
private void registerClasspathResources(String[] locations, RuntimeHints runtimeHints, ClassLoader classLoader) {
registerClasspathResources(Arrays.stream(locations), runtimeHints, classLoader);
private void registerClasspathResources(String annotation, String[] locations, RuntimeHints runtimeHints, ClassLoader classLoader) {
registerClasspathResources(annotation, Arrays.asList(locations), runtimeHints, classLoader);
}
private void registerClasspathResources(Stream<String> locations, RuntimeHints runtimeHints, ClassLoader classLoader) {
private void registerClasspathResources(String annotation, List<String> locations, RuntimeHints runtimeHints, ClassLoader classLoader) {
DefaultResourceLoader resourceLoader = new DefaultResourceLoader(classLoader);
ResourceHints resourceHints = runtimeHints.resources();
locations.map(resourceLoader::getResource)
.filter(ClassPathResource.class::isInstance)
.filter(Resource::exists)
.forEach(resourceHints::registerResource);
for (String location : locations) {
if (location.startsWith(CLASSPATH_ALL_URL_PREFIX) ||
(location.startsWith(CLASSPATH_URL_PREFIX) && (location.contains("*") || location.contains("?")))) {
if (logger.isWarnEnabled()) {
logger.warn("""
Runtime hint registration is not supported for the 'classpath*:' \
prefix or wildcards in %s locations. Please manually register a \
resource hint for each location represented by '%s'."""
.formatted(annotation, location));
}
}
else {
Resource resource = resourceLoader.getResource(location);
if (resource instanceof ClassPathResource classPathResource && classPathResource.exists()) {
resourceHints.registerPattern(classPathResource.getPath());
}
}
}
}
private void registerClasspathResourceDirectoryStructure(String directory, RuntimeHints runtimeHints) {