Update AOT resource hints with new GraalVM behavior
Prior to this commit, the resource hints for AOT applications would
generate JSON metadata with `java.util.regex.Pattern`, like:
```
{
"resources": {
"includes": [
{
"pattern": "\\Qbanner.txt\\E"
}
]
}
}
```
This regexp feature, as well as "includes" and "excludes" are not supported
anymore with the new metadata format. This commit removes the pattern
format which is now replaced by a "glob" format.
"globs" should only contain "*" (zero or more characters in a path
segment) or "**" (zero or more path segments).
Some instances of resource hint registration should be migrated as a
result.
For example, "/files/*.ext" matched both "/files/a.ext" and
"/files/folder/b.txt" in the past. The new behavior matches only the
former, unless the "/files/**/*.ext" glob pattern is used.
This commit also removes the "excludes" support, which was not widely
used and very often could lead to subtle behavior.
Closes gh-31340
This commit is contained in:
@@ -16,12 +16,10 @@
|
||||
|
||||
package org.springframework.aot.hint;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -31,16 +29,18 @@ import org.springframework.util.Assert;
|
||||
* resource on the classpath, or alternatively may contain the special
|
||||
* {@code *} character to indicate a wildcard match. For example:
|
||||
* <ul>
|
||||
* <li>{@code file.properties}: matches just the {@code file.properties}
|
||||
* <li>"file.properties": matches just the {@code file.properties}
|
||||
* file at the root of the classpath.</li>
|
||||
* <li>{@code com/example/file.properties}: matches just the
|
||||
* <li>"com/example/file.properties": matches just the
|
||||
* {@code file.properties} file in {@code com/example/}.</li>
|
||||
* <li>{@code *.properties}: matches all the files with a {@code .properties}
|
||||
* extension anywhere in the classpath.</li>
|
||||
* <li>{@code com/example/*.properties}: matches all the files with a {@code .properties}
|
||||
* extension in {@code com/example/} and its child directories at any depth.</li>
|
||||
* <li>{@code com/example/*}: matches all the files in {@code com/example/}
|
||||
* <li>"*.properties": matches all the files with a {@code .properties}
|
||||
* extension at the root of the classpath.</li>
|
||||
* <li>"com/example/*.properties": matches all the files with a {@code .properties}
|
||||
* extension in {@code com/example/}.</li>
|
||||
* <li>"com/example/{@literal **}": matches all the files in {@code com/example/}
|
||||
* and its child directories at any depth.</li>
|
||||
* <li>"com/example/{@literal **}/*.properties": matches all the files with a {@code .properties}
|
||||
* extension in {@code com/example/} and its child directories at any depth.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>A resource pattern must not start with a slash ({@code /}) unless it is the
|
||||
@@ -54,6 +54,8 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
public final class ResourcePatternHint implements ConditionalHint {
|
||||
|
||||
private static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();
|
||||
|
||||
private final String pattern;
|
||||
|
||||
@Nullable
|
||||
@@ -77,16 +79,11 @@ public final class ResourcePatternHint implements ConditionalHint {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the regex {@link Pattern} to use for identifying the resources to match.
|
||||
* Whether the given path matches the current glob pattern.
|
||||
* @param path the path to match against
|
||||
*/
|
||||
public Pattern toRegex() {
|
||||
String prefix = (this.pattern.startsWith("*") ? ".*" : "");
|
||||
String suffix = (this.pattern.endsWith("*") ? ".*" : "");
|
||||
String regex = Arrays.stream(this.pattern.split("\\*"))
|
||||
.filter(s -> !s.isEmpty())
|
||||
.map(Pattern::quote)
|
||||
.collect(Collectors.joining(".*", prefix, suffix));
|
||||
return Pattern.compile(regex);
|
||||
public boolean matches(String path) {
|
||||
return PATH_MATCHER.match(this.pattern, path);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
@@ -38,12 +38,9 @@ public final class ResourcePatternHints {
|
||||
|
||||
private final List<ResourcePatternHint> includes;
|
||||
|
||||
private final List<ResourcePatternHint> excludes;
|
||||
|
||||
|
||||
private ResourcePatternHints(Builder builder) {
|
||||
this.includes = new ArrayList<>(builder.includes);
|
||||
this.excludes = new ArrayList<>(builder.excludes);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -54,14 +51,6 @@ public final class ResourcePatternHints {
|
||||
return this.includes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the exclude patterns to use to identify the resources to match.
|
||||
* @return the exclude patterns
|
||||
*/
|
||||
public List<ResourcePatternHint> getExcludes() {
|
||||
return this.excludes;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Builder for {@link ResourcePatternHints}.
|
||||
@@ -70,13 +59,11 @@ public final class ResourcePatternHints {
|
||||
|
||||
private final Set<ResourcePatternHint> includes = new LinkedHashSet<>();
|
||||
|
||||
private final Set<ResourcePatternHint> excludes = new LinkedHashSet<>();
|
||||
|
||||
Builder() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Include resources matching the specified patterns.
|
||||
* Include resources matching the specified glob patterns.
|
||||
* @param reachableType the type that should be reachable for this hint to apply
|
||||
* @param includes the include patterns (see {@link ResourcePatternHint} documentation)
|
||||
* @return {@code this}, to facilitate method chaining
|
||||
@@ -129,7 +116,7 @@ public final class ResourcePatternHints {
|
||||
}
|
||||
|
||||
/**
|
||||
* Include resources matching the specified patterns.
|
||||
* Include resources matching the specified glob patterns.
|
||||
* @param includes the include patterns (see {@link ResourcePatternHint} documentation)
|
||||
* @return {@code this}, to facilitate method chaining
|
||||
*/
|
||||
@@ -137,28 +124,6 @@ public final class ResourcePatternHints {
|
||||
return includes(null, includes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exclude resources matching the specified patterns.
|
||||
* @param reachableType the type that should be reachable for this hint to apply
|
||||
* @param excludes the exclude patterns (see {@link ResourcePatternHint} documentation)
|
||||
* @return {@code this}, to facilitate method chaining
|
||||
*/
|
||||
public Builder excludes(@Nullable TypeReference reachableType, String... excludes) {
|
||||
List<ResourcePatternHint> newExcludes = Arrays.stream(excludes)
|
||||
.map(include -> new ResourcePatternHint(include, reachableType)).toList();
|
||||
this.excludes.addAll(newExcludes);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Exclude resources matching the specified patterns.
|
||||
* @param excludes the exclude patterns (see {@link ResourcePatternHint} documentation)
|
||||
* @return {@code this}, to facilitate method chaining
|
||||
*/
|
||||
public Builder excludes(String... excludes) {
|
||||
return excludes(null, excludes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create {@link ResourcePatternHints} based on the state of this
|
||||
* builder.
|
||||
|
||||
@@ -39,49 +39,55 @@ class ResourcePatternHintTests {
|
||||
@Test
|
||||
void rootDirectory() {
|
||||
ResourcePatternHint hint = new ResourcePatternHint("/", null);
|
||||
assertThat(hint.toRegex().asMatchPredicate())
|
||||
.accepts("/")
|
||||
.rejects("/com/example", "/file.txt");
|
||||
assertThat(hint.matches("/")).isTrue();
|
||||
assertThat(hint.matches("/com/example")).isFalse();
|
||||
assertThat(hint.matches("/file.txt")).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void fileAtRoot() {
|
||||
ResourcePatternHint hint = new ResourcePatternHint("file.properties", null);
|
||||
assertThat(hint.toRegex().asMatchPredicate())
|
||||
.accepts("file.properties")
|
||||
.rejects("com/example/file.properties", "file.prop", "another-file.properties");
|
||||
assertThat(hint.matches("file.properties")).isTrue();
|
||||
assertThat(hint.matches("com/example/file.properties")).isFalse();
|
||||
assertThat(hint.matches("file.prop")).isFalse();
|
||||
assertThat(hint.matches("another-file.properties")).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void fileInDirectory() {
|
||||
ResourcePatternHint hint = new ResourcePatternHint("com/example/file.properties", null);
|
||||
assertThat(hint.toRegex().asMatchPredicate())
|
||||
.accepts("com/example/file.properties")
|
||||
.rejects("file.properties", "com/file.properties", "com/example/another-file.properties");
|
||||
assertThat(hint.matches("com/example/file.properties")).isTrue();
|
||||
assertThat(hint.matches("file.properties")).isFalse();
|
||||
assertThat(hint.matches("com/file.properties")).isFalse();
|
||||
assertThat(hint.matches("com/example/another-file.properties")).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void extension() {
|
||||
ResourcePatternHint hint = new ResourcePatternHint("*.properties", null);
|
||||
assertThat(hint.toRegex().asMatchPredicate())
|
||||
.accepts("file.properties", "com/example/file.properties")
|
||||
.rejects("file.prop", "com/example/file.prop");
|
||||
ResourcePatternHint hint = new ResourcePatternHint("**/*.properties", null);
|
||||
assertThat(hint.matches("file.properties")).isTrue();
|
||||
assertThat(hint.matches("com/example/file.properties")).isTrue();
|
||||
assertThat(hint.matches("file.prop")).isFalse();
|
||||
assertThat(hint.matches("com/example/file.prop")).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void extensionInDirectoryAtAnyDepth() {
|
||||
ResourcePatternHint hint = new ResourcePatternHint("com/example/*.properties", null);
|
||||
assertThat(hint.toRegex().asMatchPredicate())
|
||||
.accepts("com/example/file.properties", "com/example/another/file.properties")
|
||||
.rejects("file.properties", "com/file.properties");
|
||||
assertThat(hint.matches("com/example/file.properties")).isTrue();
|
||||
assertThat(hint.matches("com/example/another/file.properties")).isFalse();
|
||||
assertThat(hint.matches("com/file.properties")).isFalse();
|
||||
assertThat(hint.matches("file.properties")).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void anyFileInDirectoryAtAnyDepth() {
|
||||
ResourcePatternHint hint = new ResourcePatternHint("com/example/*", null);
|
||||
assertThat(hint.toRegex().asMatchPredicate())
|
||||
.accepts("com/example/file.properties", "com/example/another/file.properties", "com/example/another")
|
||||
.rejects("file.properties", "com/file.properties");
|
||||
ResourcePatternHint hint = new ResourcePatternHint("com/example/**", null);
|
||||
assertThat(hint.matches("com/example/file.properties")).isTrue();
|
||||
assertThat(hint.matches("com/example/another/file.properties")).isTrue();
|
||||
assertThat(hint.matches("com/example/another")).isTrue();
|
||||
assertThat(hint.matches("file.properties")).isFalse();
|
||||
assertThat(hint.matches("com/file.properties")).isFalse();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user