From 76e848776044192e282fc6c61f6482e5a0798068 Mon Sep 17 00:00:00 2001 From: David Nestorovic Date: Mon, 28 Jul 2025 10:32:32 +0200 Subject: [PATCH] Print warning if provided pattern is invalid --- .../impl/RuntimeResourceSupport.java | 4 +- .../config/ResourceConfigurationTest.java | 4 +- .../LegacyResourceConfigurationParser.java | 4 +- .../svm/configure/ResourcesRegistry.java | 4 +- .../config/ResourceConfiguration.java | 4 +- .../CompressedGlobTrie.java | 21 +++--- .../oracle/svm/hosted/ResourcesFeature.java | 68 +++++++++++-------- .../src/com/oracle/svm/util/GlobUtils.java | 4 +- 8 files changed, 62 insertions(+), 51 deletions(-) diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/RuntimeResourceSupport.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/RuntimeResourceSupport.java index f0c2273cbf95..a32f9064ae2e 100644 --- a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/RuntimeResourceSupport.java +++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/RuntimeResourceSupport.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -56,7 +56,7 @@ static RuntimeResourceSupport singleton() { void addGlob(C condition, String module, String glob, Object origin); - void ignoreResources(C condition, String pattern); + void ignoreResources(C condition, String pattern, Object origin); void addResourceBundles(C condition, String name); diff --git a/substratevm/src/com.oracle.svm.configure.test/src/com/oracle/svm/configure/test/config/ResourceConfigurationTest.java b/substratevm/src/com.oracle.svm.configure.test/src/com/oracle/svm/configure/test/config/ResourceConfigurationTest.java index 1649fd56ed0e..0bf682128d76 100644 --- a/substratevm/src/com.oracle.svm.configure.test/src/com/oracle/svm/configure/test/config/ResourceConfigurationTest.java +++ b/substratevm/src/com.oracle.svm.configure.test/src/com/oracle/svm/configure/test/config/ResourceConfigurationTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -113,7 +113,7 @@ public void injectResource(Module module, String resourcePath, byte[] resourceCo } @Override - public void ignoreResources(UnresolvedConfigurationCondition condition, String pattern) { + public void ignoreResources(UnresolvedConfigurationCondition condition, String pattern, Object origin) { ignoredResources.add(pattern); } diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/LegacyResourceConfigurationParser.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/LegacyResourceConfigurationParser.java index 9a444c185864..bbd5fde40362 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/LegacyResourceConfigurationParser.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/LegacyResourceConfigurationParser.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -95,7 +95,7 @@ private void parseResourcesObject(Object resourcesObject, Object origin) { if (excludesObject != null) { List excludes = asList(excludesObject, "Attribute 'excludes' must be a list of resources"); for (Object object : excludes) { - parsePatternEntry(object, registry::ignoreResources, null, "'excludes' list"); + parsePatternEntry(object, (condition, pattern) -> registry.ignoreResources(condition, pattern, origin), null, "'excludes' list"); } } } else { // Old format: may be deprecated in future versions diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ResourcesRegistry.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ResourcesRegistry.java index 5fbdb3418524..72e0145e2296 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ResourcesRegistry.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ResourcesRegistry.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -51,7 +51,7 @@ default void addResources(C condition, String pattern) { } @Override - void ignoreResources(C condition, String pattern); + void ignoreResources(C condition, String pattern, Object origin); @Override void addResourceBundles(C condition, String name); diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java index 1b80fb7b6fed..1ae868bf59bb 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -94,7 +94,7 @@ public void injectResource(Module module, String resourcePath, byte[] resourceCo } @Override - public void ignoreResources(UnresolvedConfigurationCondition condition, String pattern) { + public void ignoreResources(UnresolvedConfigurationCondition condition, String pattern, Object origin) { configuration.ignoreResourcePattern(condition, pattern); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/resources/CompressedGlobTrie/CompressedGlobTrie.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/resources/CompressedGlobTrie/CompressedGlobTrie.java index 315c58243afd..c543c29f4c7f 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/resources/CompressedGlobTrie/CompressedGlobTrie.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/resources/CompressedGlobTrie/CompressedGlobTrie.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -38,8 +38,9 @@ import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; -import com.oracle.svm.core.util.UserError; +import com.oracle.svm.core.ClassLoaderSupport; import com.oracle.svm.util.GlobUtils; +import com.oracle.svm.util.LogUtils; import com.oracle.svm.util.StringUtil; /** @@ -105,12 +106,9 @@ public static GlobTrieNode build(List> patterns) { List> starPatterns = new ArrayList<>(); List> noStarPatterns = new ArrayList<>(); - List invalidPatterns = classifyPatterns(patterns, doubleStarPatterns, starPatterns, noStarPatterns); - if (!invalidPatterns.isEmpty()) { - StringBuilder sb = new StringBuilder("Error: invalid glob patterns found:" + System.lineSeparator()); - invalidPatterns.forEach(msg -> sb.append(msg).append(System.lineSeparator())); - - throw UserError.abort(sb.toString()); + List invalidPatternsErrors = classifyPatterns(patterns, doubleStarPatterns, starPatterns, noStarPatterns); + if (!invalidPatternsErrors.isEmpty()) { + invalidPatternsErrors.forEach(LogUtils::warning); } /* sort patterns in the groups based on generality */ @@ -221,7 +219,12 @@ private static List classifyPatterns(List> patterns, /* validate patterns */ String error = GlobUtils.validatePattern(patternWithInfo.pattern()); if (!error.isEmpty()) { - invalidPatterns.add(error); + if (patternWithInfo.additionalContent() instanceof ClassLoaderSupport.ConditionWithOrigin conditionWithOrigin) { + invalidPatterns.add(error + "Pattern is from: " + conditionWithOrigin.origin()); + } else { + invalidPatterns.add(error); + } + continue; } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java index da387b8655f8..af7c368eb2e3 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -53,6 +53,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.LongAdder; import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; import java.util.stream.Collectors; import org.graalvm.nativeimage.ImageSingletons; @@ -169,7 +170,7 @@ private record CompiledConditionalPattern(ConfigurationCondition condition, Reso private Set resourcePatternWorkSet = Collections.newSetFromMap(new ConcurrentHashMap<>()); private Set globWorkSet = Collections.newSetFromMap(new ConcurrentHashMap<>()); - private final Set excludedResourcePatterns = Collections.newSetFromMap(new ConcurrentHashMap<>()); + private final Set excludedResourcePatterns = Collections.newSetFromMap(new ConcurrentHashMap<>()); private int loadedConfigurations; private ImageClassLoader imageClassLoader; @@ -233,11 +234,11 @@ public void injectResource(Module module, String resourcePath, byte[] resourceCo } @Override - public void ignoreResources(ConfigurationCondition condition, String pattern) { + public void ignoreResources(ConfigurationCondition condition, String pattern, Object origin) { registerConditionalConfiguration(condition, (cnd) -> { UserError.guarantee(!sealed, "Resources ignored too late: %s", pattern); - excludedResourcePatterns.add(pattern); + excludedResourcePatterns.add(new ConditionalPattern(condition, pattern, origin)); }); } @@ -442,17 +443,11 @@ public void beforeAnalysis(BeforeAnalysisAccess a) { } /* prepare regex patterns for resource registration */ - resourcePatternWorkSet.addAll(Options.IncludeResources.getValue() - .getValuesWithOrigins() - .map(e -> new ConditionalPattern(ConfigurationCondition.alwaysTrue(), e.value(), e.origin())) - .toList()); - Set includePatterns = resourcePatternWorkSet - .stream() - .map(e -> new CompiledConditionalPattern(e.condition(), makeResourcePattern(e.pattern()), e.origin())) - .collect(Collectors.toSet()); + resourcePatternWorkSet.addAll(getPatternsFromOption(Options.IncludeResources.getValue())); + Set includePatterns = compilePatternWorkset(resourcePatternWorkSet); - excludedResourcePatterns.addAll(Options.ExcludeResources.getValue().values()); - ResourcePattern[] excludePatterns = compilePatterns(excludedResourcePatterns); + excludedResourcePatterns.addAll(getPatternsFromOption(Options.ExcludeResources.getValue())); + Set excludePatterns = compilePatternWorkset(excludedResourcePatterns); ResourceCollectorImpl collector = new ResourceCollectorImpl(includePatterns, excludePatterns); /* @@ -488,7 +483,7 @@ public void beforeAnalysis(BeforeAnalysisAccess a) { private static final class ResourceCollectorImpl extends ConditionalConfigurationRegistry implements ResourceCollector { private final Set includePatterns; - private final ResourcePattern[] excludePatterns; + private final Set excludePatterns; private static final int WATCHDOG_RESET_AFTER_EVERY_N_RESOURCES = 1000; private static final int WATCHDOG_INITIAL_WARNING_AFTER_N_SECONDS = 60; private static final int WATCHDOG_WARNING_AFTER_EVERY_N_SECONDS = 20; @@ -497,7 +492,7 @@ private static final class ResourceCollectorImpl extends ConditionalConfiguratio private volatile String currentlyProcessedEntry; ScheduledExecutorService scheduledExecutor; - private ResourceCollectorImpl(Set includePatterns, ResourcePattern[] excludePatterns) { + private ResourceCollectorImpl(Set includePatterns, Set excludePatterns) { this.includePatterns = includePatterns; this.excludePatterns = excludePatterns; @@ -542,11 +537,11 @@ public List isIncluded(Module module, String resourceName, * Once migration to glob patterns is done, this code should be removed (include and * exclude patterns) */ - for (ResourcePattern rp : excludePatterns) { - if (!rp.moduleNameMatches(moduleName)) { + for (CompiledConditionalPattern rp : excludePatterns) { + if (!rp.compiledPattern.moduleNameMatches(moduleName)) { continue; } - if (rp.pattern.matcher(resourceName).matches()) { + if (rp.compiledPattern().pattern.matcher(resourceName).matches()) { return List.of(); // nothing should match excluded resource } } @@ -606,21 +601,34 @@ public void registerIncludePattern(ConfigurationCondition condition, String modu } } - private ResourcePattern[] compilePatterns(Set patterns) { + private static List getPatternsFromOption(AccumulatingLocatableMultiOptionValue.Strings option) { + return option + .getValuesWithOrigins() + .map(e -> new ConditionalPattern(ConfigurationCondition.alwaysTrue(), e.value(), e.origin())) + .toList(); + } + + private static Set compilePatternWorkset(Set patterns) { return patterns.stream() - .filter(s -> s.length() > 0) - .map(this::makeResourcePattern) - .toList() - .toArray(new ResourcePattern[]{}); + .flatMap(e -> { + Optional resourcePattern = makeResourcePattern(e.pattern(), e.origin()); + return resourcePattern.stream().map(pattern -> new CompiledConditionalPattern(e.condition(), pattern, e.origin())); + }) + .collect(Collectors.toSet()); } - private ResourcePattern makeResourcePattern(String rawPattern) { + private static Optional makeResourcePattern(String rawPattern, Object origin) { String[] moduleNameWithPattern = SubstrateUtil.split(rawPattern, ":", 2); - if (moduleNameWithPattern.length < 2) { - return new ResourcePattern(null, Pattern.compile(moduleNameWithPattern[0])); - } else { - String moduleName = moduleNameWithPattern[0]; - return new ResourcePattern(moduleName, Pattern.compile(moduleNameWithPattern[1])); + try { + if (moduleNameWithPattern.length < 2) { + return Optional.of(new ResourcePattern(null, Pattern.compile(moduleNameWithPattern[0]))); + } else { + String moduleName = moduleNameWithPattern[0]; + return Optional.of(new ResourcePattern(moduleName, Pattern.compile(moduleNameWithPattern[1]))); + } + } catch (PatternSyntaxException e) { + LogUtils.warning("Skipping invalid pattern: " + rawPattern + " found in: " + origin); + return Optional.empty(); } } diff --git a/substratevm/src/com.oracle.svm.util/src/com/oracle/svm/util/GlobUtils.java b/substratevm/src/com.oracle.svm.util/src/com/oracle/svm/util/GlobUtils.java index 73961aa65ee1..5cd3668097fa 100644 --- a/substratevm/src/com.oracle.svm.util/src/com/oracle/svm/util/GlobUtils.java +++ b/substratevm/src/com.oracle.svm.util/src/com/oracle/svm/util/GlobUtils.java @@ -79,7 +79,7 @@ public static String validatePattern(String pattern) { return sb.toString(); } - // check if pattern contains more than 2 consecutive characters. Example: a/***/b + // check if pattern contains more than 2 consecutive * characters. Example: a/***/b if (threeConsecutiveStarsRegex.matcher(pattern).matches()) { sb.append("Pattern contains more than two consecutive * characters. "); } @@ -129,7 +129,7 @@ public static String validatePattern(String pattern) { } if (!sb.isEmpty()) { - sb.insert(0, "Pattern " + pattern + " : "); + sb.insert(0, "Invalid pattern " + pattern + ". Reasons: "); } return sb.toString();