diff --git a/spring-core/src/main/java/org/springframework/aot/hint/support/FilePatternResourceHintsRegistrar.java b/spring-core/src/main/java/org/springframework/aot/hint/support/FilePatternResourceHintsRegistrar.java index 589facfcbf1b..33371f0e80b6 100644 --- a/spring-core/src/main/java/org/springframework/aot/hint/support/FilePatternResourceHintsRegistrar.java +++ b/spring-core/src/main/java/org/springframework/aot/hint/support/FilePatternResourceHintsRegistrar.java @@ -27,60 +27,52 @@ /** * Register the necessary resource hints for loading files from the classpath, - * based on a file name prefix and an extension with convenience to support + * based on file name prefixes and file extensions with convenience to support * multiple classpath locations. * *

Only registers hints for matching classpath locations, which allows for * several locations to be provided without contributing unnecessary hints. * * @author Stephane Nicoll + * @author Sam Brannen * @since 6.0 */ public class FilePatternResourceHintsRegistrar { - private final List names; + private final List classpathLocations; - private final List locations; + private final List filePrefixes; + + private final List fileExtensions; - private final List extensions; /** - * Create a new instance for the specified file names, locations, and file - * extensions. - * @param names the file names - * @param locations the classpath locations - * @param extensions the file extensions (starting with a dot) + * Create a new instance for the specified file prefixes, classpath locations, + * and file extensions. + * @param filePrefixes the file prefixes + * @param classpathLocations the classpath locations + * @param fileExtensions the file extensions (starting with a dot) * @deprecated as of 6.0.12 in favor of {@linkplain #forClassPathLocations(String...) the builder} */ @Deprecated(since = "6.0.12", forRemoval = true) - public FilePatternResourceHintsRegistrar(List names, List locations, - List extensions) { - this.names = Builder.validateFilePrefixes(names.toArray(String[]::new)); - this.locations = Builder.validateClasspathLocations(locations.toArray(String[]::new)); - this.extensions = Builder.validateFileExtensions(extensions.toArray(String[]::new)); - } + public FilePatternResourceHintsRegistrar(List filePrefixes, List classpathLocations, + List fileExtensions) { - /** - * Configure the registrar with the specified - * {@linkplain Builder#withClasspathLocations(String...) classpath locations}. - * @param locations the classpath locations - * @return a {@link Builder} to further configure the registrar - * @since 6.0.12 - */ - public static Builder forClassPathLocations(String... locations) { - Assert.notEmpty(locations, "At least one classpath location should be specified"); - return new Builder().withClasspathLocations(locations); + this.classpathLocations = validateClasspathLocations(classpathLocations); + this.filePrefixes = validateFilePrefixes(filePrefixes); + this.fileExtensions = validateFileExtensions(fileExtensions); } + @Deprecated(since = "6.0.12", forRemoval = true) public void registerHints(ResourceHints hints, @Nullable ClassLoader classLoader) { ClassLoader classLoaderToUse = (classLoader != null ? classLoader : getClass().getClassLoader()); List includes = new ArrayList<>(); - for (String location : this.locations) { + for (String location : this.classpathLocations) { if (classLoaderToUse.getResource(location) != null) { - for (String extension : this.extensions) { - for (String name : this.names) { - includes.add(location + name + "*" + extension); + for (String filePrefix : this.filePrefixes) { + for (String fileExtension : this.fileExtensions) { + includes.add(location + filePrefix + "*" + fileExtension); } } } @@ -90,6 +82,68 @@ public void registerHints(ResourceHints hints, @Nullable ClassLoader classLoader } } + + /** + * Configure the registrar with the specified + * {@linkplain Builder#withClasspathLocations(String...) classpath locations}. + * @param classpathLocations the classpath locations + * @return a {@link Builder} to further configure the registrar + * @since 6.0.12 + * @see #forClassPathLocations(List) + */ + public static Builder forClassPathLocations(String... classpathLocations) { + return forClassPathLocations(Arrays.asList(classpathLocations)); + } + + /** + * Configure the registrar with the specified + * {@linkplain Builder#withClasspathLocations(List) classpath locations}. + * @param classpathLocations the classpath locations + * @return a {@link Builder} to further configure the registrar + * @since 6.0.12 + * @see #forClassPathLocations(String...) + */ + public static Builder forClassPathLocations(List classpathLocations) { + return new Builder().withClasspathLocations(classpathLocations); + } + + private static List validateClasspathLocations(List classpathLocations) { + Assert.notEmpty(classpathLocations, "At least one classpath location must be specified"); + List parsedLocations = new ArrayList<>(); + for (String location : classpathLocations) { + if (location.startsWith(ResourceUtils.CLASSPATH_URL_PREFIX)) { + location = location.substring(ResourceUtils.CLASSPATH_URL_PREFIX.length()); + } + if (location.startsWith("/")) { + location = location.substring(1); + } + if (!location.isEmpty() && !location.endsWith("/")) { + location = location + "/"; + } + parsedLocations.add(location); + } + return parsedLocations; + } + + private static List validateFilePrefixes(List filePrefixes) { + for (String filePrefix : filePrefixes) { + if (filePrefix.contains("*")) { + throw new IllegalArgumentException("File prefix '" + filePrefix + "' cannot contain '*'"); + } + } + return filePrefixes; + } + + private static List validateFileExtensions(List fileExtensions) { + for (String fileExtension : fileExtensions) { + if (!fileExtension.startsWith(".")) { + throw new IllegalArgumentException("Extension '" + fileExtension + "' must start with '.'"); + } + } + return fileExtensions; + } + + /** * Builder for {@link FilePatternResourceHintsRegistrar}. * @since 6.0.12 @@ -103,15 +157,34 @@ public static final class Builder { private final List fileExtensions = new ArrayList<>(); + private Builder() { + // no-op + } + + /** - * Consider the specified classpath locations. A location can either be - * a special "classpath" pseudo location or a standard location, such as - * {@code com/example/resources}. An empty String represents the root of - * the classpath. + * Consider the specified classpath locations. + *

A location can either be a special {@value ResourceUtils#CLASSPATH_URL_PREFIX} + * pseudo location or a standard location, such as {@code com/example/resources}. + * An empty String represents the root of the classpath. * @param classpathLocations the classpath locations to consider * @return this builder + * @see #withClasspathLocations(List) */ public Builder withClasspathLocations(String... classpathLocations) { + return withClasspathLocations(Arrays.asList(classpathLocations)); + } + + /** + * Consider the specified classpath locations. + *

A location can either be a special {@value ResourceUtils#CLASSPATH_URL_PREFIX} + * pseudo location or a standard location, such as {@code com/example/resources}. + * An empty String represents the root of the classpath. + * @param classpathLocations the classpath locations to consider + * @return this builder + * @see #withClasspathLocations(String...) + */ + public Builder withClasspathLocations(List classpathLocations) { this.classpathLocations.addAll(validateClasspathLocations(classpathLocations)); return this; } @@ -122,8 +195,21 @@ public Builder withClasspathLocations(String... classpathLocations) { * character. * @param filePrefixes the file prefixes to consider * @return this builder + * @see #withFilePrefixes(List) */ public Builder withFilePrefixes(String... filePrefixes) { + return withFilePrefixes(Arrays.asList(filePrefixes)); + } + + /** + * Consider the specified file prefixes. Any file whose name starts with one + * of the specified prefixes is considered. A prefix cannot contain the {@code *} + * character. + * @param filePrefixes the file prefixes to consider + * @return this builder + * @see #withFilePrefixes(String...) + */ + public Builder withFilePrefixes(List filePrefixes) { this.filePrefixes.addAll(validateFilePrefixes(filePrefixes)); return this; } @@ -133,14 +219,26 @@ public Builder withFilePrefixes(String... filePrefixes) { * {@code .} character. * @param fileExtensions the file extensions to consider * @return this builder + * @see #withFileExtensions(List) */ public Builder withFileExtensions(String... fileExtensions) { + return withFileExtensions(Arrays.asList(fileExtensions)); + } + + /** + * Consider the specified file extensions. A file extension must start with a + * {@code .} character. + * @param fileExtensions the file extensions to consider + * @return this builder + * @see #withFileExtensions(String...) + */ + public Builder withFileExtensions(List fileExtensions) { this.fileExtensions.addAll(validateFileExtensions(fileExtensions)); return this; } - FilePatternResourceHintsRegistrar build() { - Assert.notEmpty(this.classpathLocations, "At least one location should be specified"); + + private FilePatternResourceHintsRegistrar build() { return new FilePatternResourceHintsRegistrar(this.filePrefixes, this.classpathLocations, this.fileExtensions); } @@ -150,47 +248,13 @@ FilePatternResourceHintsRegistrar build() { * classpath location that resolves against the {@code ClassLoader}, files * with the configured file prefixes and extensions are registered. * @param hints the hints contributed so far for the deployment unit - * @param classLoader the classloader, or {@code null} if even the system ClassLoader isn't accessible + * @param classLoader the classloader, or {@code null} if even the system + * ClassLoader isn't accessible */ public void registerHints(ResourceHints hints, @Nullable ClassLoader classLoader) { build().registerHints(hints, classLoader); } - private static List validateClasspathLocations(String... locations) { - List parsedLocations = new ArrayList<>(); - for (String location : locations) { - if (location.startsWith(ResourceUtils.CLASSPATH_URL_PREFIX)) { - location = location.substring(ResourceUtils.CLASSPATH_URL_PREFIX.length()); - } - if (location.startsWith("/")) { - location = location.substring(1); - } - if (!location.isEmpty() && !location.endsWith("/")) { - location = location + "/"; - } - parsedLocations.add(location); - } - return parsedLocations; - } - - private static List validateFilePrefixes(String... filePrefixes) { - for (String filePrefix : filePrefixes) { - if (filePrefix.contains("*")) { - throw new IllegalArgumentException("File prefix '" + filePrefix + "' cannot contain '*'"); - } - } - return Arrays.asList(filePrefixes); - } - - private static List validateFileExtensions(String... fileExtensions) { - for (String fileExtension : fileExtensions) { - if (!fileExtension.startsWith(".")) { - throw new IllegalArgumentException("Extension '" + fileExtension + "' should start with '.'"); - } - } - return Arrays.asList(fileExtensions); - } - } } diff --git a/spring-core/src/test/java/org/springframework/aot/hint/support/FilePatternResourceHintsRegistrarTests.java b/spring-core/src/test/java/org/springframework/aot/hint/support/FilePatternResourceHintsRegistrarTests.java index 7d7c6f127a3c..c34af3867126 100644 --- a/spring-core/src/test/java/org/springframework/aot/hint/support/FilePatternResourceHintsRegistrarTests.java +++ b/spring-core/src/test/java/org/springframework/aot/hint/support/FilePatternResourceHintsRegistrarTests.java @@ -40,7 +40,7 @@ class FilePatternResourceHintsRegistrarTests { @Test void configureWithNoClasspathLocation() { assertThatIllegalArgumentException().isThrownBy(FilePatternResourceHintsRegistrar::forClassPathLocations) - .withMessageContaining("At least one classpath location should be specified"); + .withMessageContaining("At least one classpath location must be specified"); } @Test @@ -54,7 +54,7 @@ void configureWithInvalidFilePrefix() { void configureWithInvalidFileExtension() { Builder builder = FilePatternResourceHintsRegistrar.forClassPathLocations(""); assertThatIllegalArgumentException().isThrownBy(() -> builder.withFileExtensions("txt")) - .withMessageContaining("should start with '.'"); + .withMessageContaining("must start with '.'"); } @Test