Skip to content

Commit ef19782

Browse files
committed
Implement ResourceBundle module awareness
1 parent a0927e4 commit ef19782

File tree

16 files changed

+219
-176
lines changed

16 files changed

+219
-176
lines changed

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/ClassLoaderQuery.java renamed to substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/ClassLoaderSupport.java

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,28 @@
2424
*/
2525
package com.oracle.svm.core;
2626

27+
import java.util.List;
28+
import java.util.Locale;
29+
import java.util.ResourceBundle;
30+
2731
import org.graalvm.nativeimage.Platform;
2832
import org.graalvm.nativeimage.Platforms;
2933

3034
@Platforms(Platform.HOSTED_ONLY.class)
31-
public interface ClassLoaderQuery {
35+
public abstract class ClassLoaderSupport {
36+
37+
public boolean isNativeImageClassLoader(ClassLoader classLoader) {
38+
ClassLoader loader = classLoader;
39+
while (loader != null) {
40+
if (isNativeImageClassLoaderImpl(loader)) {
41+
return true;
42+
}
43+
loader = loader.getParent();
44+
}
45+
return false;
46+
}
47+
48+
protected abstract boolean isNativeImageClassLoaderImpl(ClassLoader classLoader);
3249

33-
boolean isNativeImageClassLoader(ClassLoader c);
50+
public abstract List<ResourceBundle> getResourceBundle(String bundleName, Locale locale);
3451
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/RuntimeAssertionsSupport.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ private boolean desiredAssertionStatusImpl(String name, boolean fallback) {
184184
}
185185

186186
private boolean desiredAssertionStatusImpl(String name, ClassLoader classLoader) {
187-
boolean isNativeImageClassLoader = ImageSingletons.lookup(ClassLoaderQuery.class).isNativeImageClassLoader(classLoader);
187+
boolean isNativeImageClassLoader = ImageSingletons.lookup(ClassLoaderSupport.class).isNativeImageClassLoader(classLoader);
188188
return desiredAssertionStatusImpl(name, isNativeImageClassLoader ? defaultAssertionStatus : systemAssertionStatus);
189189
}
190190

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/LocalizationFeature.java

Lines changed: 8 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
import org.graalvm.nativeimage.Platforms;
7070
import org.graalvm.nativeimage.hosted.Feature;
7171

72+
import com.oracle.svm.core.ClassLoaderSupport;
7273
import com.oracle.svm.core.annotate.Substitute;
7374
import com.oracle.svm.core.jdk.localization.compression.GzipBundleCompression;
7475
import com.oracle.svm.core.jdk.localization.substitutions.Target_sun_util_locale_provider_LocaleServiceProviderPool_OptimizedLocaleMode;
@@ -77,7 +78,6 @@
7778
import com.oracle.svm.core.option.OptionUtils;
7879
import com.oracle.svm.core.util.UserError;
7980
import com.oracle.svm.core.util.VMError;
80-
import com.oracle.svm.util.ModuleSupport;
8181
import com.oracle.svm.util.ReflectionUtil;
8282

8383
import jdk.vm.ci.meta.ResolvedJavaField;
@@ -456,29 +456,16 @@ public void prepareBundle(String baseName, Collection<Locale> wantedLocales) {
456456

457457
boolean somethingFound = false;
458458
for (Locale locale : wantedLocales) {
459-
ResourceBundle resourceBundle;
459+
List<ResourceBundle> resourceBundle;
460460
try {
461-
resourceBundle = ModuleSupport.getResourceBundle(baseName, locale, Thread.currentThread().getContextClassLoader());
461+
resourceBundle = ImageSingletons.lookup(ClassLoaderSupport.class).getResourceBundle(baseName, locale);
462462
} catch (MissingResourceException mre) {
463-
if (!baseName.contains("/")) {
464-
// fallthrough
465-
continue;
466-
}
467-
// Due to a possible bug in the JDK, bundle names not following proper naming
468-
// convention
469-
// need to be
470-
// converted to fully qualified class names before loading can succeed.
471-
// see GR-24211
472-
String dotBundleName = baseName.replace("/", ".");
473-
try {
474-
resourceBundle = ModuleSupport.getResourceBundle(dotBundleName, locale, Thread.currentThread().getContextClassLoader());
475-
} catch (MissingResourceException ex) {
476-
// fallthrough
477-
continue;
478-
}
463+
continue;
464+
}
465+
somethingFound = !resourceBundle.isEmpty();
466+
for (ResourceBundle bundle : resourceBundle) {
467+
prepareBundle(baseName, bundle, locale);
479468
}
480-
somethingFound = true;
481-
prepareBundle(baseName, resourceBundle, locale);
482469
}
483470

484471
if (!somethingFound) {
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
package com.oracle.svm.hosted;
2+
3+
import java.util.ArrayDeque;
4+
import java.util.ArrayList;
5+
import java.util.Collections;
6+
import java.util.Deque;
7+
import java.util.HashMap;
8+
import java.util.HashSet;
9+
import java.util.List;
10+
import java.util.Locale;
11+
import java.util.Map;
12+
import java.util.MissingResourceException;
13+
import java.util.ResourceBundle;
14+
import java.util.Set;
15+
import java.util.stream.Collectors;
16+
17+
import com.oracle.svm.core.ClassLoaderSupport;
18+
19+
import jdk.internal.module.Modules;
20+
21+
public final class ClassLoaderSupportImpl extends ClassLoaderSupport {
22+
23+
private final NativeImageClassLoaderSupport classLoaderSupport;
24+
private final Map<String, Set<Module>> packageToModules;
25+
26+
ClassLoaderSupportImpl(NativeImageClassLoaderSupport classLoaderSupport) {
27+
this.classLoaderSupport = classLoaderSupport;
28+
packageToModules = new HashMap<>();
29+
buildPackageToModulesMap(classLoaderSupport);
30+
}
31+
32+
@Override
33+
protected boolean isNativeImageClassLoaderImpl(ClassLoader loader) {
34+
return loader == classLoaderSupport.getClassLoader() || loader instanceof NativeImageSystemClassLoader;
35+
}
36+
37+
@Override
38+
public List<ResourceBundle> getResourceBundle(String bundleSpec, Locale locale) {
39+
String[] specParts = bundleSpec.split(":", 2);
40+
String moduleName;
41+
String bundleName;
42+
if (specParts.length > 1) {
43+
moduleName = specParts[0];
44+
bundleName = specParts[1];
45+
} else {
46+
moduleName = null;
47+
bundleName = specParts[0];
48+
}
49+
String packageName = packageName(bundleName);
50+
if (packageName == null) {
51+
throw new MissingResourceException("ResourceBundle does not seem to be a fully qualified class name.", bundleName, locale.toLanguageTag());
52+
}
53+
Set<Module> modules;
54+
if (moduleName != null) {
55+
modules = classLoaderSupport.findModule(moduleName).stream().collect(Collectors.toSet());
56+
} else {
57+
modules = packageToModules.getOrDefault(packageName, Collections.emptySet());
58+
}
59+
if (modules.isEmpty()) {
60+
throw new MissingResourceException("ResourceBundle cannot be found.", bundleSpec, locale.toLanguageTag());
61+
}
62+
ArrayList<ResourceBundle> resourceBundles = new ArrayList<>();
63+
for (Module module : modules) {
64+
Module exportTargetModule = ClassLoaderSupportImpl.class.getModule();
65+
if (!module.isExported(packageName, exportTargetModule)) {
66+
System.out.printf("!!! Open for ResourceBundle access: %s:%s%n", module.getName(), packageName);
67+
Modules.addOpens(module, packageName, exportTargetModule);
68+
}
69+
resourceBundles.add(ResourceBundle.getBundle(bundleName, locale, module));
70+
}
71+
return resourceBundles;
72+
}
73+
74+
private static String packageName(String bundleName) {
75+
int classSep = bundleName.replace('/', '.').lastIndexOf('.');
76+
if (classSep == -1) {
77+
/* The bundle is not specified via a java.class or java.properties format. */
78+
return null;
79+
}
80+
return bundleName.substring(0, classSep);
81+
}
82+
83+
private void buildPackageToModulesMap(NativeImageClassLoaderSupport classLoaderSupport) {
84+
for (ModuleLayer layer : allLayers(classLoaderSupport.moduleLayerForImageBuild)) {
85+
for (Module module : layer.modules()) {
86+
for (String packageName : module.getDescriptor().packages()) {
87+
addToPackageNameModules(module, packageName);
88+
}
89+
}
90+
}
91+
dumpPackageNameModulesMapping();
92+
}
93+
94+
private static List<ModuleLayer> allLayers(ModuleLayer moduleLayer) {
95+
/** Implementation taken from {@link ModuleLayer#layers()} */
96+
List<ModuleLayer> allLayers = new ArrayList<>();
97+
Set<ModuleLayer> visited = new HashSet<>();
98+
Deque<ModuleLayer> stack = new ArrayDeque<>();
99+
visited.add(moduleLayer);
100+
stack.push(moduleLayer);
101+
102+
while (!stack.isEmpty()) {
103+
ModuleLayer layer = stack.pop();
104+
allLayers.add(layer);
105+
106+
// push in reverse order
107+
for (int i = layer.parents().size() - 1; i >= 0; i--) {
108+
ModuleLayer parent = layer.parents().get(i);
109+
if (!visited.contains(parent)) {
110+
visited.add(parent);
111+
stack.push(parent);
112+
}
113+
}
114+
}
115+
return allLayers;
116+
}
117+
118+
private void addToPackageNameModules(Module moduleName, String packageName) {
119+
Set<Module> prevValue = packageToModules.get(packageName);
120+
if (prevValue == null) {
121+
/* Mostly packageName is only used in a single module */
122+
packageToModules.put(packageName, Collections.singleton(moduleName));
123+
} else if (prevValue.size() == 1) {
124+
/* Transition to HashSet - happens rarely */
125+
HashSet<Module> newValue = new HashSet<>();
126+
newValue.add(prevValue.iterator().next());
127+
newValue.add(moduleName);
128+
packageToModules.put(packageName, newValue);
129+
} else if (prevValue.size() > 1) {
130+
/* Add to exiting HashSet - happens rarely */
131+
prevValue.add(moduleName);
132+
}
133+
}
134+
135+
public void dumpPackageNameModulesMapping() {
136+
packageToModules.entrySet().stream()
137+
.sorted(Map.Entry.comparingByKey())
138+
.map(e -> e.getKey() + " -> " + e.getValue().stream()
139+
.map(Module::getName)
140+
.collect(Collectors.joining(", ")))
141+
.forEach(System.out::println);
142+
}
143+
}

substratevm/src/com.oracle.svm.hosted.jdk11/src/com/oracle/svm/hosted/NativeImageClassLoaderSupport.java

Lines changed: 1 addition & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,9 @@
3333
import java.lang.module.ModuleReference;
3434
import java.nio.file.Path;
3535
import java.nio.file.Paths;
36-
import java.util.ArrayDeque;
37-
import java.util.ArrayList;
3836
import java.util.Arrays;
3937
import java.util.Collections;
40-
import java.util.Deque;
41-
import java.util.HashMap;
42-
import java.util.HashSet;
4338
import java.util.List;
44-
import java.util.Map;
4539
import java.util.Objects;
4640
import java.util.Optional;
4741
import java.util.Set;
@@ -65,27 +59,16 @@ public class NativeImageClassLoaderSupport extends AbstractNativeImageClassLoade
6559
private final List<Path> buildmp;
6660

6761
private final ClassLoader classLoader;
68-
private final ModuleLayer moduleLayerForImageBuild;
69-
private final Map<String, Set<Module>> packageToModuleNames;
62+
final ModuleLayer moduleLayerForImageBuild;
7063

7164
NativeImageClassLoaderSupport(ClassLoader defaultSystemClassLoader, String[] classpath, String[] modulePath) {
7265
super(defaultSystemClassLoader, classpath);
7366

74-
packageToModuleNames = new HashMap<>();
75-
7667
imagemp = Arrays.stream(modulePath).map(Paths::get).collect(Collectors.toUnmodifiableList());
7768
buildmp = Arrays.stream(System.getProperty("jdk.module.path", "").split(File.pathSeparator)).map(Paths::get).collect(Collectors.toUnmodifiableList());
7869

7970
ModuleLayer moduleLayer = createModuleLayer(imagemp.toArray(Path[]::new), classPathClassLoader);
8071
adjustBootLayerQualifiedExports(moduleLayer);
81-
for (ModuleLayer layer : allLayers(moduleLayer)) {
82-
for (Module module : layer.modules()) {
83-
for (String packageName : module.getDescriptor().packages()) {
84-
addToPackageNameModules(module, packageName);
85-
}
86-
}
87-
}
88-
// dumpPackageNameModulesMapping();
8972
moduleLayerForImageBuild = moduleLayer;
9073
classLoader = getSingleClassloader(moduleLayer);
9174
}
@@ -102,56 +85,6 @@ private static ModuleLayer createModuleLayer(Path[] modulePaths, ClassLoader par
10285
return ModuleLayer.defineModulesWithOneLoader(configuration, List.of(ModuleLayer.boot()), parent).layer();
10386
}
10487

105-
private List<ModuleLayer> allLayers(ModuleLayer moduleLayer) {
106-
/** Implementation taken from {@link ModuleLayer#layers()} */
107-
List<ModuleLayer> allLayers = new ArrayList<>();
108-
Set<ModuleLayer> visited = new HashSet<>();
109-
Deque<ModuleLayer> stack = new ArrayDeque<>();
110-
visited.add(moduleLayer);
111-
stack.push(moduleLayer);
112-
113-
while (!stack.isEmpty()) {
114-
ModuleLayer layer = stack.pop();
115-
allLayers.add(layer);
116-
117-
// push in reverse order
118-
for (int i = layer.parents().size() - 1; i >= 0; i--) {
119-
ModuleLayer parent = layer.parents().get(i);
120-
if (!visited.contains(parent)) {
121-
visited.add(parent);
122-
stack.push(parent);
123-
}
124-
}
125-
}
126-
return allLayers;
127-
}
128-
129-
private void addToPackageNameModules(Module moduleName, String packageName) {
130-
Set<Module> prevValue = packageToModuleNames.get(packageName);
131-
if (prevValue == null) {
132-
/* Mostly packageName is only used in a single module */
133-
packageToModuleNames.put(packageName, Collections.singleton(moduleName));
134-
} else if (prevValue.size() == 1) {
135-
/* Transition to HashSet - happens rarely */
136-
HashSet<Module> newValue = new HashSet<>();
137-
newValue.add(prevValue.iterator().next());
138-
newValue.add(moduleName);
139-
packageToModuleNames.put(packageName, newValue);
140-
} else if (prevValue.size() > 1) {
141-
/* Add to exiting HashSet - happens rarely */
142-
prevValue.add(moduleName);
143-
}
144-
}
145-
146-
public void dumpPackageNameModulesMapping() {
147-
packageToModuleNames.entrySet().stream()
148-
.sorted(Map.Entry.comparingByKey())
149-
.map(e -> e.getKey() + " -> " + e.getValue().stream()
150-
.map(Module::getName)
151-
.collect(Collectors.joining(", ")))
152-
.forEach(System.out::println);
153-
}
154-
15588
private void adjustBootLayerQualifiedExports(ModuleLayer layer) {
15689
/*
15790
* For all qualified exports packages of modules in the the boot layer we check if layer
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.oracle.svm.hosted;
2+
3+
import java.util.Collections;
4+
import java.util.List;
5+
import java.util.Locale;
6+
import java.util.ResourceBundle;
7+
8+
import com.oracle.svm.core.ClassLoaderSupport;
9+
10+
public final class ClassLoaderSupportImpl extends ClassLoaderSupport {
11+
12+
private final ClassLoader imageClassLoader;
13+
14+
ClassLoaderSupportImpl(NativeImageClassLoaderSupport classLoaderSupport) {
15+
this.imageClassLoader = classLoaderSupport.getClassLoader();
16+
}
17+
18+
@Override
19+
protected boolean isNativeImageClassLoaderImpl(ClassLoader loader) {
20+
return loader == imageClassLoader || loader instanceof NativeImageSystemClassLoader;
21+
}
22+
23+
@Override
24+
public List<ResourceBundle> getResourceBundle(String bundleName, Locale locale) {
25+
return Collections.singletonList(ResourceBundle.getBundle(bundleName, locale, imageClassLoader));
26+
}
27+
}

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ImageClassLoader.java

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@
4949
import org.graalvm.nativeimage.Platform;
5050
import org.graalvm.nativeimage.Platforms;
5151

52-
import com.oracle.svm.core.ClassLoaderQuery;
5352
import com.oracle.svm.core.TypeResult;
5453

5554
public final class ImageClassLoader {
@@ -434,23 +433,3 @@ public void processAddExportsAndAddOpens(OptionValues parsedHostedOptions) {
434433
}
435434
}
436435

437-
class ClassLoaderQueryImpl implements ClassLoaderQuery {
438-
439-
private final ClassLoader imageClassLoader;
440-
441-
ClassLoaderQueryImpl(ClassLoader imageClassLoader) {
442-
this.imageClassLoader = imageClassLoader;
443-
}
444-
445-
@Override
446-
public boolean isNativeImageClassLoader(ClassLoader classLoader) {
447-
ClassLoader loader = classLoader;
448-
while (loader != null) {
449-
if (loader == imageClassLoader || loader instanceof NativeImageSystemClassLoader) {
450-
return true;
451-
}
452-
loader = loader.getParent();
453-
}
454-
return false;
455-
}
456-
}

0 commit comments

Comments
 (0)