Skip to content

Commit 26c059c

Browse files
committed
Use enum set of ConfigurationParserOption to customize ConfigurationParser behaviour
1 parent 9c3da40 commit 26c059c

File tree

32 files changed

+261
-142
lines changed

32 files changed

+261
-142
lines changed

substratevm/src/com.oracle.svm.configure.test/src/com/oracle/svm/configure/test/config/ResourceConfigurationTest.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import java.io.PipedReader;
3030
import java.io.PipedWriter;
3131
import java.util.Collection;
32+
import java.util.EnumSet;
3233
import java.util.LinkedList;
3334
import java.util.List;
3435
import java.util.Locale;
@@ -38,6 +39,7 @@
3839
import org.junit.Assert;
3940
import org.junit.Test;
4041

42+
import com.oracle.svm.configure.ConfigurationParserOption;
4143
import com.oracle.svm.configure.ResourceConfigurationParser;
4244
import com.oracle.svm.configure.ResourcesRegistry;
4345
import com.oracle.svm.configure.config.ResourceConfiguration;
@@ -135,7 +137,8 @@ public void addClassBasedResourceBundle(UnresolvedConfigurationCondition conditi
135137
}
136138
};
137139

138-
ResourceConfigurationParser<UnresolvedConfigurationCondition> rcp = ResourceConfigurationParser.create(false, ConfigurationConditionResolver.identityResolver(), registry, true);
140+
ResourceConfigurationParser<UnresolvedConfigurationCondition> rcp = ResourceConfigurationParser.create(false, ConfigurationConditionResolver.identityResolver(), registry,
141+
EnumSet.of(ConfigurationParserOption.STRICT_CONFIGURATION));
139142
writerThread.start();
140143
rcp.parseAndRegister(pr);
141144

substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ConfigurationBase.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
package com.oracle.svm.configure;
2626

2727
import java.io.IOException;
28+
import java.util.EnumSet;
2829
import java.util.function.Consumer;
2930

3031
import org.graalvm.nativeimage.impl.UnresolvedConfigurationCondition;
@@ -70,7 +71,7 @@ public T copyAndFilter(P predicate) {
7071
return copyAnd(copy -> copy.removeIf(predicate));
7172
}
7273

73-
public abstract ConfigurationParser createParser(boolean strictMetadata);
74+
public abstract ConfigurationParser createParser(boolean strictMetadata, EnumSet<ConfigurationParserOption> parserOptions);
7475

7576
public abstract boolean supportsCombinedFile();
7677

substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ConfigurationParser.java

Lines changed: 21 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,6 @@
2424
*/
2525
package com.oracle.svm.configure;
2626

27-
import static org.graalvm.nativeimage.impl.UnresolvedConfigurationCondition.TYPE_REACHABLE_KEY;
28-
import static org.graalvm.nativeimage.impl.UnresolvedConfigurationCondition.TYPE_REACHED_KEY;
29-
3027
import java.io.BufferedReader;
3128
import java.io.FileNotFoundException;
3229
import java.io.IOException;
@@ -37,6 +34,7 @@
3734
import java.net.URL;
3835
import java.util.Collection;
3936
import java.util.Collections;
37+
import java.util.EnumSet;
4038
import java.util.HashMap;
4139
import java.util.HashSet;
4240
import java.util.List;
@@ -62,7 +60,6 @@ public static InputStream openStream(URI uri) throws IOException {
6260
throw new IllegalArgumentException("For security reasons, reading configurations is not supported from URIs with protocol: " + url.getProtocol());
6361
}
6462

65-
public static final String CONDITIONAL_KEY = "condition";
6663
public static final String NAME_KEY = "name";
6764
public static final String TYPE_KEY = "type";
6865
public static final String PROXY_KEY = "proxy";
@@ -75,10 +72,25 @@ public static InputStream openStream(URI uri) throws IOException {
7572
public static final String MODULE_KEY = "module";
7673
public static final String GLOB_KEY = "glob";
7774
private final Map<String, Set<String>> seenUnknownAttributesByType = new HashMap<>();
78-
private final boolean strictSchema;
75+
private final EnumSet<ConfigurationParserOption> parserOptions;
76+
77+
protected ConfigurationParser(EnumSet<ConfigurationParserOption> parserOptions) {
78+
this.parserOptions = parserOptions;
79+
}
7980

80-
protected ConfigurationParser(boolean strictConfiguration) {
81-
this.strictSchema = strictConfiguration;
81+
/**
82+
* Defines the options supported by this parser. A subclass can override this method to declare
83+
* additional supported options.
84+
*/
85+
protected EnumSet<ConfigurationParserOption> supportedOptions() {
86+
return EnumSet.of(ConfigurationParserOption.STRICT_CONFIGURATION);
87+
}
88+
89+
protected final boolean checkOption(ConfigurationParserOption option) {
90+
if (!supportedOptions().contains(option)) {
91+
throw new IllegalArgumentException(String.format("Tried to check option %s but it was not declared as a supported option", option));
92+
}
93+
return parserOptions.contains(option);
8294
}
8395

8496
public void parseAndRegister(URI uri) throws IOException {
@@ -175,7 +187,7 @@ public static void checkHasExactlyOneAttribute(EconomicMap<String, Object> map,
175187
* @param message message to be displayed.
176188
*/
177189
protected void warnOrFailOnSchemaError(String message) {
178-
if (strictSchema) {
190+
if (checkOption(ConfigurationParserOption.STRICT_CONFIGURATION)) {
179191
failOnSchemaError(message);
180192
} else {
181193
LogUtils.warning(message);
@@ -221,40 +233,7 @@ protected static long asLong(Object value, String propertyName) {
221233
throw new JsonParserException("Invalid long value '" + value + "' for element '" + propertyName + "'");
222234
}
223235

224-
protected UnresolvedConfigurationCondition parseCondition(EconomicMap<String, Object> data, boolean runtimeCondition) {
225-
Object conditionData = data.get(CONDITIONAL_KEY);
226-
if (conditionData != null) {
227-
EconomicMap<String, Object> conditionObject = asMap(conditionData, "Attribute 'condition' must be an object");
228-
if (conditionObject.containsKey(TYPE_REACHABLE_KEY) && conditionObject.containsKey(TYPE_REACHED_KEY)) {
229-
failOnSchemaError("condition can not have both '" + TYPE_REACHED_KEY + "' and '" + TYPE_REACHABLE_KEY + "' set.");
230-
}
231-
232-
if (conditionObject.containsKey(TYPE_REACHED_KEY)) {
233-
if (!runtimeCondition) {
234-
failOnSchemaError("'" + TYPE_REACHED_KEY + "' condition cannot be used in older schemas. Please migrate the file to the latest schema.");
235-
}
236-
Object object = conditionObject.get(TYPE_REACHED_KEY);
237-
var condition = parseTypeContents(object);
238-
if (condition.isPresent()) {
239-
String className = ((NamedConfigurationTypeDescriptor) condition.get()).name();
240-
return UnresolvedConfigurationCondition.create(className);
241-
}
242-
} else if (conditionObject.containsKey(TYPE_REACHABLE_KEY)) {
243-
if (runtimeCondition && !Options.TreatAllTypeReachableConditionsAsTypeReached.getValue()) {
244-
failOnSchemaError("'" + TYPE_REACHABLE_KEY + "' condition can not be used with the latest schema. Please use '" + TYPE_REACHED_KEY + "'.");
245-
}
246-
Object object = conditionObject.get(TYPE_REACHABLE_KEY);
247-
var condition = parseTypeContents(object);
248-
if (condition.isPresent()) {
249-
String className = ((NamedConfigurationTypeDescriptor) condition.get()).name();
250-
return UnresolvedConfigurationCondition.create(className, Options.TreatAllTypeReachableConditionsAsTypeReached.getValue());
251-
}
252-
}
253-
}
254-
return UnresolvedConfigurationCondition.alwaysTrue();
255-
}
256-
257-
private static JsonParserException failOnSchemaError(String message) {
236+
protected static JsonParserException failOnSchemaError(String message) {
258237
throw new JsonParserException(message);
259238
}
260239

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package com.oracle.svm.configure;
26+
27+
/**
28+
* {@link ConfigurationParser} options that control parsing behaviour. Options are not necessarily
29+
* supported by all parsers.
30+
*/
31+
public enum ConfigurationParserOption {
32+
/**
33+
* Fail when a configuration file has incorrect schema (e.g., extraneous fields).
34+
*/
35+
STRICT_CONFIGURATION,
36+
37+
/**
38+
* Log a warning for each reflection element (e.g., class, field) that could not be resolved.
39+
*/
40+
PRINT_MISSING_ELEMENTS,
41+
42+
/**
43+
* Treat the legacy "typeReachable" condition as a "typeReached" condition checked at run time.
44+
*/
45+
TREAT_ALL_TYPE_REACHABLE_CONDITIONS_AS_TYPE_REACHED,
46+
47+
/**
48+
* Treat the "name" entry in a legacy reflection configuration as a "type" entry.
49+
*/
50+
TREAT_ALL_NAME_ENTRIES_AS_TYPE
51+
}

substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/LegacyReflectionConfigurationParser.java

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
import java.net.URI;
2828
import java.util.Arrays;
29+
import java.util.EnumSet;
2930
import java.util.List;
3031
import java.util.Optional;
3132

@@ -44,12 +45,15 @@ final class LegacyReflectionConfigurationParser<C, T> extends ReflectionConfigur
4445
"allPublicClasses", "methods", "queriedMethods", "fields", CONDITIONAL_KEY,
4546
"queryAllDeclaredConstructors", "queryAllPublicConstructors", "queryAllDeclaredMethods", "queryAllPublicMethods", "unsafeAllocated", "serializable");
4647

47-
private final boolean treatAllNameEntriesAsType;
48+
LegacyReflectionConfigurationParser(ConfigurationConditionResolver<C> conditionResolver, ReflectionConfigurationParserDelegate<C, T> delegate, EnumSet<ConfigurationParserOption> parserOptions) {
49+
super(conditionResolver, delegate, parserOptions);
50+
}
4851

49-
LegacyReflectionConfigurationParser(ConfigurationConditionResolver<C> conditionResolver, ReflectionConfigurationParserDelegate<C, T> delegate, boolean strictConfiguration,
50-
boolean printMissingElements, boolean treatAllNameEntriesAsType) {
51-
super(conditionResolver, delegate, strictConfiguration, printMissingElements);
52-
this.treatAllNameEntriesAsType = treatAllNameEntriesAsType;
52+
@Override
53+
protected EnumSet<ConfigurationParserOption> supportedOptions() {
54+
EnumSet<ConfigurationParserOption> base = super.supportedOptions();
55+
base.add(ConfigurationParserOption.TREAT_ALL_NAME_ENTRIES_AS_TYPE);
56+
return base;
5357
}
5458

5559
@Override
@@ -61,7 +65,7 @@ public void parseAndRegister(Object json, URI origin) {
6165
protected void parseClass(EconomicMap<String, Object> data) {
6266
checkAttributes(data, "reflection class descriptor object", List.of(NAME_KEY), OPTIONAL_REFLECT_CONFIG_OBJECT_ATTRS);
6367

64-
Optional<TypeDescriptorWithOrigin> type = parseName(data, treatAllNameEntriesAsType);
68+
Optional<TypeDescriptorWithOrigin> type = parseName(data, checkOption(ConfigurationParserOption.TREAT_ALL_NAME_ENTRIES_AS_TYPE));
6569
if (type.isEmpty()) {
6670
return;
6771
}

substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/LegacyResourceConfigurationParser.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
import java.net.URI;
2828
import java.util.Collections;
29+
import java.util.EnumSet;
2930
import java.util.List;
3031
import java.util.function.BiConsumer;
3132

@@ -38,8 +39,8 @@
3839
import com.oracle.svm.util.TypeResult;
3940

4041
final class LegacyResourceConfigurationParser<C> extends ResourceConfigurationParser<C> {
41-
LegacyResourceConfigurationParser(ConfigurationConditionResolver<C> conditionResolver, ResourcesRegistry<C> registry, boolean strictConfiguration) {
42-
super(conditionResolver, registry, strictConfiguration);
42+
LegacyResourceConfigurationParser(ConfigurationConditionResolver<C> conditionResolver, ResourcesRegistry<C> registry, EnumSet<ConfigurationParserOption> parserOptions) {
43+
super(conditionResolver, registry, parserOptions);
4344
}
4445

4546
@Override

substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/LegacySerializationConfigurationParser.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import java.net.URI;
2828
import java.util.Arrays;
2929
import java.util.Collections;
30+
import java.util.EnumSet;
3031
import java.util.List;
3132

3233
import org.graalvm.collections.EconomicMap;
@@ -46,9 +47,9 @@ final class LegacySerializationConfigurationParser<C> extends SerializationConfi
4647

4748
private final ProxyConfigurationParser<C> proxyConfigurationParser;
4849

49-
LegacySerializationConfigurationParser(ConfigurationConditionResolver<C> conditionResolver, RuntimeSerializationSupport<C> serializationSupport, boolean strictConfiguration) {
50-
super(conditionResolver, serializationSupport, strictConfiguration);
51-
this.proxyConfigurationParser = new ProxyConfigurationParser<>(conditionResolver, strictConfiguration, serializationSupport::registerProxyClass);
50+
LegacySerializationConfigurationParser(ConfigurationConditionResolver<C> conditionResolver, RuntimeSerializationSupport<C> serializationSupport, EnumSet<ConfigurationParserOption> parserOptions) {
51+
super(conditionResolver, serializationSupport, parserOptions);
52+
this.proxyConfigurationParser = new ProxyConfigurationParser<>(conditionResolver, parserOptions, serializationSupport::registerProxyClass);
5253
}
5354

5455
@Override

substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/PredefinedClassesConfigurationParser.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import java.nio.file.Path;
3434
import java.util.Arrays;
3535
import java.util.Collections;
36+
import java.util.EnumSet;
3637

3738
import org.graalvm.collections.EconomicMap;
3839

@@ -46,8 +47,8 @@ public static InputStream openClassdataStream(URI baseUri, String providedHash)
4647

4748
private final PredefinedClassesRegistry registry;
4849

49-
public PredefinedClassesConfigurationParser(PredefinedClassesRegistry registry, boolean strictConfiguration) {
50-
super(strictConfiguration);
50+
public PredefinedClassesConfigurationParser(PredefinedClassesRegistry registry, EnumSet<ConfigurationParserOption> parserOptions) {
51+
super(parserOptions);
5152
this.registry = registry;
5253
}
5354

substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ProxyConfigurationParser.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
import java.net.URI;
2828
import java.util.Collections;
29+
import java.util.EnumSet;
2930
import java.util.List;
3031
import java.util.function.BiConsumer;
3132
import java.util.stream.Collectors;
@@ -42,15 +43,14 @@
4243
* Parses JSON describing lists of interfaces and passes them to the given consumer (e.g., to add
4344
* them to a registry).
4445
*/
45-
public final class ProxyConfigurationParser<C> extends ConfigurationParser {
46+
public final class ProxyConfigurationParser<C> extends ConditionalConfigurationParser {
4647

4748
private final ConfigurationConditionResolver<C> conditionResolver;
4849

4950
private final BiConsumer<C, List<String>> proxyConfigConsumer;
5051

51-
public ProxyConfigurationParser(ConfigurationConditionResolver<C> conditionResolver, boolean strictConfiguration,
52-
BiConsumer<C, List<String>> proxyConfigConsumer) {
53-
super(strictConfiguration);
52+
public ProxyConfigurationParser(ConfigurationConditionResolver<C> conditionResolver, EnumSet<ConfigurationParserOption> parserOptions, BiConsumer<C, List<String>> proxyConfigConsumer) {
53+
super(parserOptions);
5454
this.proxyConfigConsumer = proxyConfigConsumer;
5555
this.conditionResolver = conditionResolver;
5656
}

substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ReflectionConfigurationParser.java

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import java.util.ArrayList;
2828
import java.util.Arrays;
2929
import java.util.Collections;
30+
import java.util.EnumSet;
3031
import java.util.List;
3132
import java.util.stream.Collectors;
3233

@@ -42,28 +43,32 @@
4243
* Parses JSON describing classes, methods and fields and delegates their registration to a
4344
* {@link ReflectionConfigurationParserDelegate}.
4445
*/
45-
public abstract class ReflectionConfigurationParser<C, T> extends ConfigurationParser {
46+
public abstract class ReflectionConfigurationParser<C, T> extends ConditionalConfigurationParser {
4647
private static final String CONSTRUCTOR_NAME = "<init>";
4748

4849
protected final ConfigurationConditionResolver<C> conditionResolver;
4950
protected final ReflectionConfigurationParserDelegate<C, T> delegate;
50-
private final boolean printMissingElements;
5151

52-
public ReflectionConfigurationParser(ConfigurationConditionResolver<C> conditionResolver, ReflectionConfigurationParserDelegate<C, T> delegate, boolean strictConfiguration,
53-
boolean printMissingElements) {
54-
super(strictConfiguration);
52+
public ReflectionConfigurationParser(ConfigurationConditionResolver<C> conditionResolver, ReflectionConfigurationParserDelegate<C, T> delegate, EnumSet<ConfigurationParserOption> parserOptions) {
53+
super(parserOptions);
5554
this.conditionResolver = conditionResolver;
56-
this.printMissingElements = printMissingElements;
5755
this.delegate = delegate;
5856
}
5957

58+
@Override
59+
protected EnumSet<ConfigurationParserOption> supportedOptions() {
60+
EnumSet<ConfigurationParserOption> base = super.supportedOptions();
61+
base.add(ConfigurationParserOption.PRINT_MISSING_ELEMENTS);
62+
return base;
63+
}
64+
6065
public static <C, T> ReflectionConfigurationParser<C, T> create(String combinedFileKey, boolean strictMetadata,
6166
ConfigurationConditionResolver<C> conditionResolver, ReflectionConfigurationParserDelegate<C, T> delegate,
62-
boolean strictConfiguration, boolean printMissingElements, boolean treatAllEntriesAsType) {
67+
EnumSet<ConfigurationParserOption> parserOptions) {
6368
if (strictMetadata) {
64-
return new ReflectionMetadataParser<>(combinedFileKey, conditionResolver, delegate, strictConfiguration, printMissingElements);
69+
return new ReflectionMetadataParser<>(combinedFileKey, conditionResolver, delegate, parserOptions);
6570
} else {
66-
return new LegacyReflectionConfigurationParser<>(conditionResolver, delegate, strictConfiguration, printMissingElements, treatAllEntriesAsType);
71+
return new LegacyReflectionConfigurationParser<>(conditionResolver, delegate, parserOptions);
6772
}
6873
}
6974

@@ -189,7 +194,7 @@ private void handleMissingElement(String message) {
189194
}
190195

191196
protected void handleMissingElement(String msg, Throwable cause) {
192-
if (printMissingElements) {
197+
if (checkOption(ConfigurationParserOption.PRINT_MISSING_ELEMENTS)) {
193198
String message = msg;
194199
if (cause != null) {
195200
message += " Reason: " + formatError(cause) + '.';

0 commit comments

Comments
 (0)