3434import java .util .Arrays ;
3535import java .util .Collections ;
3636import java .util .Date ;
37+ import java .util .EnumSet ;
3738import java .util .Enumeration ;
3839import java .util .List ;
3940import java .util .Objects ;
4041import java .util .Set ;
42+ import java .util .function .BiConsumer ;
4143import java .util .stream .Collectors ;
4244import java .util .stream .StreamSupport ;
4345
5355import com .oracle .svm .core .ClassLoaderSupport .ConditionWithOrigin ;
5456import com .oracle .svm .core .MissingRegistrationUtils ;
5557import com .oracle .svm .core .SubstrateOptions ;
58+ import com .oracle .svm .core .SubstrateUtil ;
5659import com .oracle .svm .core .configure .ConditionalRuntimeValue ;
5760import com .oracle .svm .core .configure .RuntimeConditionSet ;
5861import com .oracle .svm .core .feature .AutomaticallyRegisteredFeature ;
6770import com .oracle .svm .core .jdk .resources .CompressedGlobTrie .CompressedGlobTrie ;
6871import com .oracle .svm .core .jdk .resources .CompressedGlobTrie .GlobTrieNode ;
6972import com .oracle .svm .core .jdk .resources .CompressedGlobTrie .GlobUtils ;
73+ import com .oracle .svm .core .layeredimagesingleton .LayeredImageSingletonBuilderFlags ;
74+ import com .oracle .svm .core .layeredimagesingleton .MultiLayeredImageSingleton ;
75+ import com .oracle .svm .core .layeredimagesingleton .UnsavedSingleton ;
7076import com .oracle .svm .core .util .ImageHeapMap ;
7177import com .oracle .svm .core .util .VMError ;
7278import com .oracle .svm .util .LogUtils ;
7884 * Registered resources are then available from DynamicHub#getResource classes and
7985 * {@link Target_java_lang_ClassLoader class loaders}.
8086 */
81- public final class Resources {
87+ public final class Resources implements MultiLayeredImageSingleton , UnsavedSingleton {
8288
8389 private static final int INVALID_TIMESTAMP = -1 ;
8490 public static final char RESOURCES_INTERNAL_PATH_SEPARATOR = '/' ;
8591
92+ /**
93+ * @return the singleton corresponding to this layer's resources in a layered build, the unique
94+ * singleton otherwise
95+ */
96+ @ Platforms (Platform .HOSTED_ONLY .class )
8697 public static Resources singleton () {
8798 return ImageSingletons .lookup (Resources .class );
8899 }
89100
101+ /**
102+ * @return an array of singletons corresponding to all layers in a layered build, or an array
103+ * with a single element otherwise
104+ */
105+ public static Resources [] layeredSingletons () {
106+ assert !SubstrateUtil .HOSTED : "Accessing all layers resources at build time" ;
107+ return MultiLayeredImageSingleton .getAllLayers (Resources .class );
108+ }
109+
90110 /**
91111 * The hosted map used to collect registered resources. Using a {@link ModuleResourceKey} of
92112 * (module, resourceName) provides implementations for {@code hashCode()} and {@code equals()}
@@ -139,20 +159,31 @@ public void setResourcesTrieRoot(GlobTrieNode<ConditionWithOrigin> resourcesTrie
139159 this .resourcesTrieRoot = resourcesTrieRoot ;
140160 }
141161
142- public EconomicMap <ModuleResourceKey , ConditionalRuntimeValue <ResourceStorageEntryBase >> getResourceStorage () {
143- return resources ;
162+ public void forEachResource (BiConsumer <ModuleResourceKey , ConditionalRuntimeValue <ResourceStorageEntryBase >> action ) {
163+ MapCursor <ModuleResourceKey , ConditionalRuntimeValue <ResourceStorageEntryBase >> entries = resources .getEntries ();
164+ while (entries .advance ()) {
165+ action .accept (entries .getKey (), entries .getValue ());
166+ }
167+ }
168+
169+ @ Platforms (Platform .HOSTED_ONLY .class )
170+ public ConditionalRuntimeValue <ResourceStorageEntryBase > getResource (ModuleResourceKey storageKey ) {
171+ return resources .get (storageKey );
144172 }
145173
174+ @ Platforms (Platform .HOSTED_ONLY .class )
146175 public Iterable <ConditionalRuntimeValue <ResourceStorageEntryBase >> resources () {
147176 return resources .getValues ();
148177 }
149178
179+ @ Platforms (Platform .HOSTED_ONLY .class )
150180 public int count () {
151181 return resources .size ();
152182 }
153183
154- public long getLastModifiedTime () {
155- return lastModifiedTime ;
184+ public static long getLastModifiedTime () {
185+ var singletons = layeredSingletons ();
186+ return singletons [singletons .length - 1 ].lastModifiedTime ;
156187 }
157188
158189 public static String moduleName (Module module ) {
@@ -169,6 +200,7 @@ public static ModuleResourceKey createStorageKey(Module module, String resourceN
169200 return new ModuleResourceKey (m , resourceName );
170201 }
171202
203+ @ Platforms (Platform .HOSTED_ONLY .class )
172204 public static Set <String > getIncludedResourcesModules () {
173205 return StreamSupport .stream (singleton ().resources .getKeys ().spliterator (), false )
174206 .map (ModuleResourceKey ::module )
@@ -320,7 +352,7 @@ private static boolean wasAlreadyInCanonicalForm(String resourceName, String can
320352 return resourceName .equals (canonicalResourceName ) || removeTrailingSlash (resourceName ).equals (canonicalResourceName );
321353 }
322354
323- public ResourceStorageEntryBase getAtRuntime (String name , boolean throwOnMissing ) {
355+ public static ResourceStorageEntryBase getAtRuntime (String name , boolean throwOnMissing ) {
324356 return getAtRuntime (null , name , throwOnMissing );
325357 }
326358
@@ -330,29 +362,31 @@ public ResourceStorageEntryBase getAtRuntime(String name, boolean throwOnMissing
330362 * {@link MissingResourceRegistrationError}. This is needed because different modules can be
331363 * tried on the same resource name, causing an unexpected exception if we throw directly.
332364 */
333- public ResourceStorageEntryBase getAtRuntime (Module module , String resourceName , boolean throwOnMissing ) {
365+ public static ResourceStorageEntryBase getAtRuntime (Module module , String resourceName , boolean throwOnMissing ) {
334366 VMError .guarantee (ImageInfo .inImageRuntimeCode (), "This function should be used only at runtime." );
335367 String canonicalResourceName = toCanonicalForm (resourceName );
336368 String moduleName = moduleName (module );
337- ConditionalRuntimeValue <ResourceStorageEntryBase > entry = resources . get ( createStorageKey ( module , canonicalResourceName ) );
369+ ConditionalRuntimeValue <ResourceStorageEntryBase > entry = getEntry ( module , canonicalResourceName );
338370 if (entry == null ) {
339371 if (MissingRegistrationUtils .throwMissingRegistrationErrors ()) {
340- MapCursor <RequestedPattern , RuntimeConditionSet > cursor = requestedPatterns .getEntries ();
341- while (cursor .advance ()) {
342- RequestedPattern moduleResourcePair = cursor .getKey ();
343- if (Objects .equals (moduleName , moduleResourcePair .module ) &&
344- ((matchResource (moduleResourcePair .resource , resourceName ) || matchResource (moduleResourcePair .resource , canonicalResourceName )) &&
345- cursor .getValue ().satisfied ())) {
346- return null ;
372+ for (var r : layeredSingletons ()) {
373+ MapCursor <RequestedPattern , RuntimeConditionSet > cursor = r .requestedPatterns .getEntries ();
374+ while (cursor .advance ()) {
375+ RequestedPattern moduleResourcePair = cursor .getKey ();
376+ if (Objects .equals (moduleName , moduleResourcePair .module ) &&
377+ ((matchResource (moduleResourcePair .resource , resourceName ) || matchResource (moduleResourcePair .resource , canonicalResourceName )) &&
378+ cursor .getValue ().satisfied ())) {
379+ return null ;
380+ }
347381 }
348- }
349382
350- String glob = GlobUtils .transformToTriePath (resourceName , moduleName );
351- String canonicalGlob = GlobUtils .transformToTriePath (canonicalResourceName , moduleName );
352- GlobTrieNode <ConditionWithOrigin > globsTrie = getResourcesTrieRoot ();
353- if (CompressedGlobTrie .match (globsTrie , glob ) ||
354- CompressedGlobTrie .match (globsTrie , canonicalGlob )) {
355- return null ;
383+ String glob = GlobUtils .transformToTriePath (resourceName , moduleName );
384+ String canonicalGlob = GlobUtils .transformToTriePath (canonicalResourceName , moduleName );
385+ GlobTrieNode <ConditionWithOrigin > globsTrie = r .getResourcesTrieRoot ();
386+ if (CompressedGlobTrie .match (globsTrie , glob ) ||
387+ CompressedGlobTrie .match (globsTrie , canonicalGlob )) {
388+ return null ;
389+ }
356390 }
357391
358392 return missingMetadata (resourceName , throwOnMissing );
@@ -389,6 +423,16 @@ public ResourceStorageEntryBase getAtRuntime(Module module, String resourceName,
389423 return unconditionalEntry ;
390424 }
391425
426+ private static ConditionalRuntimeValue <ResourceStorageEntryBase > getEntry (Module module , String canonicalResourceName ) {
427+ for (var r : layeredSingletons ()) {
428+ ConditionalRuntimeValue <ResourceStorageEntryBase > entry = r .resources .get (createStorageKey (module , canonicalResourceName ));
429+ if (entry != null ) {
430+ return entry ;
431+ }
432+ }
433+ return null ;
434+ }
435+
392436 private static ResourceStorageEntryBase missingMetadata (String resourceName , boolean throwOnMissing ) {
393437 if (throwOnMissing ) {
394438 MissingResourceRegistrationUtils .missingResource (resourceName );
@@ -407,11 +451,11 @@ private static URL createURL(Module module, String resourceName, int index) {
407451 }
408452 }
409453
410- public URL createURL (String resourceName ) {
454+ public static URL createURL (String resourceName ) {
411455 return createURL (null , resourceName );
412456 }
413457
414- public URL createURL (Module module , String resourceName ) {
458+ public static URL createURL (Module module , String resourceName ) {
415459 if (resourceName == null ) {
416460 return null ;
417461 }
@@ -420,12 +464,12 @@ public URL createURL(Module module, String resourceName) {
420464 return urls .hasMoreElements () ? urls .nextElement () : null ;
421465 }
422466
423- public InputStream createInputStream (String resourceName ) {
467+ public static InputStream createInputStream (String resourceName ) {
424468 return createInputStream (null , resourceName );
425469 }
426470
427471 /* Avoid pulling in the URL class when only an InputStream is needed. */
428- public InputStream createInputStream (Module module , String resourceName ) {
472+ public static InputStream createInputStream (Module module , String resourceName ) {
429473 if (resourceName == null ) {
430474 return null ;
431475 }
@@ -458,11 +502,11 @@ public InputStream createInputStream(Module module, String resourceName) {
458502 return data .isEmpty () ? null : new ByteArrayInputStream (data .get (0 ));
459503 }
460504
461- public Enumeration <URL > createURLs (String resourceName ) {
505+ public static Enumeration <URL > createURLs (String resourceName ) {
462506 return createURLs (null , resourceName );
463507 }
464508
465- public Enumeration <URL > createURLs (Module module , String resourceName ) {
509+ public static Enumeration <URL > createURLs (Module module , String resourceName ) {
466510 if (resourceName == null ) {
467511 return null ;
468512 }
@@ -541,6 +585,11 @@ private static boolean matchResource(String pattern, String resource) {
541585
542586 return resource .startsWith (start ) && resource .endsWith (end );
543587 }
588+
589+ @ Override
590+ public EnumSet <LayeredImageSingletonBuilderFlags > getImageBuilderFlags () {
591+ return LayeredImageSingletonBuilderFlags .ALL_ACCESS ;
592+ }
544593}
545594
546595@ AutomaticallyRegisteredFeature
0 commit comments