Skip to content

Commit 809500b

Browse files
committed
[GR-63591] Include resource bundles in resource configuration
PullRequest: graal/20791
2 parents 5bde507 + d227453 commit 809500b

File tree

10 files changed

+118
-85
lines changed

10 files changed

+118
-85
lines changed

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/json/JsonPrinter.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,12 @@ public interface JsonPrinter<T> {
5555
* @see JsonWriter#print(Object)
5656
*/
5757
static <T> void printCollection(JsonWriter writer, Collection<T> collection, Comparator<T> comparator, JsonPrinter<T> elementPrinter) throws IOException {
58-
if (collection.isEmpty()) {
58+
printCollection(writer, collection, comparator, elementPrinter, true, true);
59+
}
60+
61+
/* Utility method to allow printing multiple collections into the same array */
62+
static <T> void printCollection(JsonWriter writer, Collection<T> collection, Comparator<T> comparator, JsonPrinter<T> elementPrinter, boolean arrayStart, boolean arrayEnd) throws IOException {
63+
if (collection.isEmpty() && arrayStart && arrayEnd) {
5964
writer.append("[]");
6065
return;
6166
}
@@ -66,7 +71,9 @@ static <T> void printCollection(JsonWriter writer, Collection<T> collection, Com
6671
((List<T>) ordered).sort(comparator);
6772
}
6873

69-
writer.appendArrayStart();
74+
if (arrayStart) {
75+
writer.appendArrayStart();
76+
}
7077
boolean separator = false;
7178
for (T t : ordered) {
7279
if (separator) {
@@ -75,6 +82,8 @@ static <T> void printCollection(JsonWriter writer, Collection<T> collection, Com
7582
elementPrinter.print(t, writer);
7683
separator = true;
7784
}
78-
writer.appendArrayEnd();
85+
if (arrayEnd) {
86+
writer.appendArrayEnd();
87+
}
7988
}
8089
}

docs/reference-manual/native-image/ReachabilityMetadata.md

Lines changed: 9 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -130,8 +130,7 @@ The _reachability-metadata.json_ configuration contains a single object with one
130130
```json
131131
{
132132
"reflection":[],
133-
"resources":[],
134-
"bundles":[]
133+
"resources":[]
135134
}
136135
```
137136
@@ -562,13 +561,13 @@ For each registered resource you get:
562561
Java localization support (`java.util.ResourceBundle`) enables to load L10N resources and show messages localized for a specific _locale_.
563562
Native Image needs knowledge of the resource bundles that your application uses so that it can include appropriate resources and program elements to the application.
564563

565-
A simple bundle can be specified in the `bundles` section of _reachability-metadata.json_:
564+
A simple bundle can be specified in the `resources` section of _reachability-metadata.json_:
566565

567566
```json
568567
{
569-
"bundles": [
568+
"resources": [
570569
{
571-
"name":"your.pkg.Bundle"
570+
"bundle": "your.pkg.Bundle"
572571
}
573572
]
574573
}
@@ -577,26 +576,15 @@ A simple bundle can be specified in the `bundles` section of _reachability-metad
577576
To request a bundle from a specific module:
578577
```json
579578
{
580-
"bundles": [
579+
"resources": [
581580
{
582-
"name":"app.module:module.pkg.Bundle"
581+
"bundle": "app.module:module.pkg.Bundle"
583582
}
584583
]
585584
}
586585
```
587586

588-
By default, resource bundles are included for all locales that are [included into the image](#locales).
589-
Below is the example how to include only specific locales for a bundle:
590-
```json
591-
{
592-
"bundles": [
593-
{
594-
"name": "specific.locales.Bundle",
595-
"locales": ["en", "de", "sk"]
596-
}
597-
]
598-
}
599-
```
587+
Resource bundles are included for all locales that are [included into the image](#locales).
600588

601589
### Locales
602590

@@ -737,12 +725,9 @@ See below is a sample reachability metadata configuration that you can use in _r
737725
{
738726
"module": "optional.module.of.a.resource",
739727
"glob": "path1/level*/**"
740-
}
741-
],
742-
"bundles": [
728+
},
743729
{
744-
"name": "fully.qualified.bundle.name",
745-
"locales": ["en", "de", "other_optional_locales"]
730+
"bundle": "fully.qualified.bundle.name"
746731
}
747732
]
748733
}

docs/reference-manual/native-image/assets/reachability-metadata-schema-v1.1.0.json

Lines changed: 56 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -64,37 +64,69 @@
6464
"type": "array",
6565
"default": [],
6666
"items": {
67-
"title": "Resource that should be available",
68-
"type": "object",
69-
"properties": {
70-
"reason": {
71-
"title": "Reason for the resource's inclusion in the metadata",
72-
"$ref": "#/$defs/reason"
73-
},
74-
"condition": {
75-
"title": "Condition under which the resource should be registered for runtime access",
76-
"$ref": "#/$defs/condition"
77-
},
78-
"module": {
79-
"title": "Module containing the resource",
80-
"type": "string",
81-
"default": ""
82-
},
83-
"glob": {
84-
"title": "Resource name or pattern matching multiple resources (accepts * and ** wildcards)",
85-
"type": "string"
67+
"oneOf": [
68+
{
69+
"title": "Resource that should be available",
70+
"type": "object",
71+
"properties": {
72+
"reason": {
73+
"title": "Reason for the resource's inclusion in the metadata",
74+
"$ref": "#/$defs/reason"
75+
},
76+
"condition": {
77+
"title": "Condition under which the resource should be registered for runtime access",
78+
"$ref": "#/$defs/condition"
79+
},
80+
"module": {
81+
"title": "Module containing the resource",
82+
"type": "string",
83+
"default": ""
84+
},
85+
"glob": {
86+
"title": "Resource name or pattern matching multiple resources (accepts * and ** wildcards)",
87+
"type": "string"
88+
}
89+
},
90+
"required": [
91+
"glob"
92+
],
93+
"additionalProperties": false
94+
},
95+
{
96+
"title": "Resource bundle that should be available",
97+
"type": "object",
98+
"properties": {
99+
"reason": {
100+
"title": "Reason for the resource bundle's inclusion in the metadata",
101+
"$ref": "#/$defs/reason"
102+
},
103+
"condition": {
104+
"title": "Condition under which the resource bundle should be registered for runtime access",
105+
"$ref": "#/$defs/condition"
106+
},
107+
"module": {
108+
"title": "Module containing the resource bundle",
109+
"type": "string",
110+
"default": ""
111+
},
112+
"bundle": {
113+
"title": "Resource bundle name",
114+
"type": "string"
115+
}
116+
},
117+
"required": [
118+
"bundle"
119+
],
120+
"additionalProperties": false
86121
}
87-
},
88-
"required": [
89-
"glob"
90-
],
91-
"additionalProperties": false
122+
]
92123
}
93124
},
94125
"bundles": {
95126
"title": "Metadata to ensure resource bundles are available",
96127
"type": "array",
97128
"default": [],
129+
"deprecated": true,
98130
"items": {
99131
"title": "Resource bundle that should be available",
100132
"type": "object",

substratevm/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ This changelog summarizes major changes to GraalVM Native Image.
2727
* (GR-64787) Enable `--install-exit-handlers` by default for executables and deprecate the option. If shared libraries were using this flag, the same functionality can be restored by using `-H:+InstallExitHandlers`.
2828
* (GR-47881) Remove the total number of loaded types, fields, and methods from the build output, deprecated these metrics in the build output schema, and removed already deprecated build output metrics.
2929
* (GR-64619) Missing registration errors are now subclasses of `LinkageError`
30+
* (GR-63591) Resource bundle registration is now included as part of the `"resources"` section of _reachability-metadata.json_. When this is the case, the bundle name is specified using the `"bundle"` field.
3031

3132
## GraalVM for JDK 24 (Internal Version 24.2.0)
3233
* (GR-59717) Added `DuringSetupAccess.registerObjectReachabilityHandler` to allow registering a callback that is executed when an object of a specified type is marked as reachable during heap scanning.

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ public static InputStream openStream(URI uri) throws IOException {
7272
public static final String GLOBS_KEY = "globs";
7373
public static final String MODULE_KEY = "module";
7474
public static final String GLOB_KEY = "glob";
75+
public static final String BUNDLE_KEY = "bundle";
7576
private final Map<String, Set<String>> seenUnknownAttributesByType = new HashMap<>();
7677
private final EnumSet<ConfigurationParserOption> parserOptions;
7778

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

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,16 +62,17 @@ protected ResourceConfigurationParser(ConfigurationConditionResolver<C> conditio
6262
protected void parseBundlesObject(Object bundlesObject) {
6363
List<Object> bundles = asList(bundlesObject, "Attribute 'bundles' must be a list of bundles");
6464
for (Object bundle : bundles) {
65-
parseBundle(bundle);
65+
parseBundle(bundle, false);
6666
}
6767
}
6868

6969
protected abstract UnresolvedConfigurationCondition parseCondition(EconomicMap<String, Object> condition);
7070

71-
private void parseBundle(Object bundle) {
71+
protected void parseBundle(Object bundle, boolean inResourcesSection) {
7272
EconomicMap<String, Object> resource = asMap(bundle, "Elements of 'bundles' list must be a bundle descriptor object");
73-
checkAttributes(resource, "bundle descriptor object", Collections.singletonList("name"), Arrays.asList("locales", "classNames", "condition"));
74-
String basename = asString(resource.get("name"));
73+
String bundleNameAttribute = inResourcesSection ? BUNDLE_KEY : NAME_KEY;
74+
checkAttributes(resource, "bundle descriptor object", Collections.singletonList(bundleNameAttribute), Arrays.asList("locales", "classNames", "condition"));
75+
String basename = asString(resource.get(bundleNameAttribute));
7576
TypeResult<C> resolvedConfigurationCondition = conditionResolver.resolveCondition(parseCondition(resource));
7677
if (!resolvedConfigurationCondition.isPresent()) {
7778
return;
@@ -121,7 +122,7 @@ protected interface GlobPatternConsumer<T> {
121122
void accept(T a, String b, String c);
122123
}
123124

124-
private void parseGlobEntry(Object data, GlobPatternConsumer<C> resourceRegistry) {
125+
protected void parseGlobEntry(Object data, GlobPatternConsumer<C> resourceRegistry) {
125126
EconomicMap<String, Object> globObject = asMap(data, "Elements of 'globs' list must be a glob descriptor objects");
126127
checkAttributes(globObject, "glob resource descriptor object", Collections.singletonList(GLOB_KEY),
127128
List.of(CONDITIONAL_KEY, MODULE_KEY));

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

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

2727
import java.net.URI;
2828
import java.util.EnumSet;
29+
import java.util.List;
2930

3031
import org.graalvm.collections.EconomicMap;
3132

@@ -40,7 +41,15 @@ final class ResourceMetadataParser<C> extends ResourceConfigurationParser<C> {
4041
public void parseAndRegister(Object json, URI origin) {
4142
Object resourcesJson = getFromGlobalFile(json, RESOURCES_KEY);
4243
if (resourcesJson != null) {
43-
parseGlobsObject(resourcesJson, origin);
44+
List<Object> globsAndBundles = asList(resourcesJson, "'resources' section must be a list of glob pattern or bundle descriptors");
45+
for (Object object : globsAndBundles) {
46+
EconomicMap<String, Object> globOrBundle = asMap(object, "Elements of 'resources' list must be glob pattern or bundle descriptor objects");
47+
if (globOrBundle.containsKey(GLOB_KEY)) {
48+
parseGlobEntry(object, (condition, module, glob) -> registry.addGlob(condition, module, glob, origin));
49+
} else if (globOrBundle.containsKey(BUNDLE_KEY)) {
50+
parseBundle(globOrBundle, true);
51+
}
52+
}
4453
}
4554
Object bundlesJson = getFromGlobalFile(json, BUNDLES_KEY);
4655
if (bundlesJson != null) {

substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ConfigurationSet.java

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -200,12 +200,7 @@ public static List<Path> writeConfigurationToAllPaths(Function<ConfigurationFile
200200
}
201201

202202
public static void printConfigurationToCombinedFile(JsonPrintable config, ConfigurationFile configFile, JsonWriter writer) throws IOException {
203-
if (!configFile.equals(ConfigurationFile.RESOURCES)) {
204-
/*
205-
* Resources are printed at the top level of the object, not in a defined field
206-
*/
207-
writer.quote(configFile.getFieldName()).appendFieldSeparator();
208-
}
203+
writer.quote(configFile.getFieldName()).appendFieldSeparator();
209204
config.printJson(writer);
210205
}
211206

substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@
2525
package com.oracle.svm.configure.config;
2626

2727
import static com.oracle.svm.configure.ConfigurationParser.BUNDLES_KEY;
28+
import static com.oracle.svm.configure.ConfigurationParser.BUNDLE_KEY;
2829
import static com.oracle.svm.configure.ConfigurationParser.GLOBS_KEY;
30+
import static com.oracle.svm.configure.ConfigurationParser.NAME_KEY;
2931
import static com.oracle.svm.configure.ConfigurationParser.RESOURCES_KEY;
3032

3133
import java.io.IOException;
@@ -298,19 +300,14 @@ public void addBundle(UnresolvedConfigurationCondition condition, String basenam
298300
}
299301
}
300302

301-
private void addBundle(UnresolvedConfigurationCondition condition, String baseName) {
303+
public void addBundle(UnresolvedConfigurationCondition condition, String baseName) {
302304
getOrCreateBundleConfig(condition, baseName);
303305
}
304306

305307
private void addClassResourceBundle(UnresolvedConfigurationCondition condition, String basename, String className) {
306308
getOrCreateBundleConfig(condition, basename).classNames.add(className);
307309
}
308310

309-
public void addBundle(UnresolvedConfigurationCondition condition, String baseName, String queriedLocale) {
310-
BundleConfiguration config = getOrCreateBundleConfig(condition, baseName);
311-
config.locales.add(queriedLocale);
312-
}
313-
314311
private BundleConfiguration getOrCreateBundleConfig(UnresolvedConfigurationCondition condition, String baseName) {
315312
ConditionalElement<String> key = new ConditionalElement<>(condition, baseName);
316313
return bundles.computeIfAbsent(key, cond -> new BundleConfiguration(condition, baseName));
@@ -342,19 +339,23 @@ public boolean anyBundleMatches(UnresolvedConfigurationCondition condition, Stri
342339

343340
@Override
344341
public void printJson(JsonWriter writer) throws IOException {
345-
printGlobsJson(writer, true);
346-
writer.appendSeparator();
347-
printBundlesJson(writer, true);
342+
JsonPrinter.printCollection(writer, addedGlobs, ConditionalElement.comparator(ResourceEntry.comparator()), (p, w) -> conditionalGlobElementJson(p, w, true), true, bundles.isEmpty());
343+
if (!bundles.isEmpty()) {
344+
if (!addedGlobs.isEmpty()) {
345+
writer.appendSeparator();
346+
}
347+
JsonPrinter.printCollection(writer, bundles.keySet(), ConditionalElement.comparator(String::compareTo), (p, w) -> printResourceBundle(bundles.get(p), w, true), false, true);
348+
}
348349
}
349350

350351
@Override
351352
public void printLegacyJson(JsonWriter writer) throws IOException {
352353
writer.appendObjectStart();
353354
printResourcesJson(writer);
354355
writer.appendSeparator();
355-
printBundlesJson(writer, false);
356+
printBundlesJson(writer);
356357
writer.appendSeparator();
357-
printGlobsJson(writer, false);
358+
printGlobsJson(writer);
358359
writer.appendObjectEnd();
359360
}
360361

@@ -370,14 +371,14 @@ void printResourcesJson(JsonWriter writer) throws IOException {
370371
writer.appendObjectEnd();
371372
}
372373

373-
void printBundlesJson(JsonWriter writer, boolean combinedFile) throws IOException {
374+
void printBundlesJson(JsonWriter writer) throws IOException {
374375
writer.quote(BUNDLES_KEY).appendFieldSeparator();
375-
JsonPrinter.printCollection(writer, bundles.keySet(), ConditionalElement.comparator(), (p, w) -> printResourceBundle(bundles.get(p), w, combinedFile));
376+
JsonPrinter.printCollection(writer, bundles.keySet(), ConditionalElement.comparator(), (p, w) -> printResourceBundle(bundles.get(p), w, false));
376377
}
377378

378-
void printGlobsJson(JsonWriter writer, boolean combinedFile) throws IOException {
379-
writer.quote(combinedFile ? RESOURCES_KEY : GLOBS_KEY).appendFieldSeparator();
380-
JsonPrinter.printCollection(writer, addedGlobs, ConditionalElement.comparator(ResourceEntry.comparator()), (p, w) -> conditionalGlobElementJson(p, w, combinedFile));
379+
void printGlobsJson(JsonWriter writer) throws IOException {
380+
writer.quote(GLOBS_KEY).appendFieldSeparator();
381+
JsonPrinter.printCollection(writer, addedGlobs, ConditionalElement.comparator(ResourceEntry.comparator()), (p, w) -> conditionalGlobElementJson(p, w, false));
381382
}
382383

383384
@Override
@@ -388,12 +389,12 @@ public ConfigurationParser createParser(boolean combinedFileSchema, EnumSet<Conf
388389
private static void printResourceBundle(BundleConfiguration config, JsonWriter writer, boolean combinedFile) throws IOException {
389390
writer.appendObjectStart();
390391
ConfigurationConditionPrintable.printConditionAttribute(config.condition, writer, combinedFile);
391-
writer.quote("name").appendFieldSeparator().quote(config.baseName);
392-
if (!config.locales.isEmpty()) {
392+
writer.quote(combinedFile ? BUNDLE_KEY : NAME_KEY).appendFieldSeparator().quote(config.baseName);
393+
if (!combinedFile && !config.locales.isEmpty()) {
393394
writer.appendSeparator().quote("locales").appendFieldSeparator();
394395
JsonPrinter.printCollection(writer, config.locales, Comparator.naturalOrder(), (String p, JsonWriter w) -> w.quote(p));
395396
}
396-
if (!config.classNames.isEmpty()) {
397+
if (!combinedFile && !config.classNames.isEmpty()) {
397398
writer.appendSeparator().quote("classNames").appendFieldSeparator();
398399
JsonPrinter.printCollection(writer, config.classNames, Comparator.naturalOrder(), (String p, JsonWriter w) -> w.quote(p));
399400
}
@@ -411,7 +412,7 @@ public boolean supportsCombinedFile() {
411412
return false;
412413
}
413414
for (ResourceConfiguration.BundleConfiguration bundleConfiguration : bundles.values()) {
414-
if (!bundleConfiguration.classNames.isEmpty()) {
415+
if (!bundleConfiguration.classNames.isEmpty() || !bundleConfiguration.locales.isEmpty()) {
415416
return false;
416417
}
417418
}

0 commit comments

Comments
 (0)