35
35
import java .util .Date ;
36
36
import java .util .EnumSet ;
37
37
import java .util .Enumeration ;
38
+ import java .util .HashMap ;
39
+ import java .util .HashSet ;
38
40
import java .util .List ;
41
+ import java .util .Map ;
39
42
import java .util .Objects ;
40
43
import java .util .Set ;
41
44
import java .util .function .BiConsumer ;
42
45
import java .util .function .Function ;
43
46
import java .util .stream .Collectors ;
44
47
import java .util .stream .StreamSupport ;
45
48
46
- import com .oracle .svm .core .encoder .SymbolEncoder ;
47
49
import org .graalvm .collections .EconomicMap ;
48
50
import org .graalvm .collections .MapCursor ;
49
51
import org .graalvm .nativeimage .ImageInfo ;
59
61
import com .oracle .svm .core .SubstrateUtil ;
60
62
import com .oracle .svm .core .configure .ConditionalRuntimeValue ;
61
63
import com .oracle .svm .core .configure .RuntimeConditionSet ;
64
+ import com .oracle .svm .core .encoder .SymbolEncoder ;
62
65
import com .oracle .svm .core .feature .AutomaticallyRegisteredFeature ;
63
66
import com .oracle .svm .core .feature .InternalFeature ;
67
+ import com .oracle .svm .core .imagelayer .ImageLayerBuildingSupport ;
64
68
import com .oracle .svm .core .jdk .resources .MissingResourceRegistrationError ;
65
69
import com .oracle .svm .core .jdk .resources .MissingResourceRegistrationUtils ;
66
70
import com .oracle .svm .core .jdk .resources .ResourceExceptionEntry ;
69
73
import com .oracle .svm .core .jdk .resources .ResourceURLConnection ;
70
74
import com .oracle .svm .core .jdk .resources .CompressedGlobTrie .CompressedGlobTrie ;
71
75
import com .oracle .svm .core .jdk .resources .CompressedGlobTrie .GlobTrieNode ;
76
+ import com .oracle .svm .core .layeredimagesingleton .ImageSingletonLoader ;
77
+ import com .oracle .svm .core .layeredimagesingleton .ImageSingletonWriter ;
72
78
import com .oracle .svm .core .layeredimagesingleton .LayeredImageSingletonBuilderFlags ;
73
79
import com .oracle .svm .core .layeredimagesingleton .LayeredImageSingletonSupport ;
74
80
import com .oracle .svm .core .layeredimagesingleton .MultiLayeredImageSingleton ;
75
- import com .oracle .svm .core .layeredimagesingleton .UnsavedSingleton ;
76
81
import com .oracle .svm .core .metadata .MetadataTracer ;
77
82
import com .oracle .svm .core .util .ImageHeapMap ;
78
83
import com .oracle .svm .core .util .VMError ;
87
92
* Registered resources are then available from DynamicHub#getResource classes and
88
93
* {@link Target_java_lang_ClassLoader class loaders}.
89
94
*/
90
- public final class Resources implements MultiLayeredImageSingleton , UnsavedSingleton {
95
+ public final class Resources implements MultiLayeredImageSingleton {
91
96
92
97
private static final int INVALID_TIMESTAMP = -1 ;
93
98
public static final char RESOURCES_INTERNAL_PATH_SEPARATOR = '/' ;
94
- private final SymbolEncoder encoder = SymbolEncoder .singleton ();
99
+ private static final String RESOURCE_KEYS = "resourceKeys" ;
100
+ private static final String RESOURCE_REGISTRATION_STATES = "resourceRegistrationStates" ;
101
+ private static final String PATTERNS = "patterns" ;
102
+
103
+ @ Platforms (Platform .HOSTED_ONLY .class ) //
104
+ private SymbolEncoder encoder ;
95
105
96
106
/**
97
107
* @return the singleton corresponding to this layer's resources in a layered build, the unique
@@ -121,6 +131,27 @@ public static Resources[] layeredSingletons() {
121
131
private final EconomicMap <ModuleResourceKey , ConditionalRuntimeValue <ResourceStorageEntryBase >> resources = ImageHeapMap .createNonLayeredMap ();
122
132
private final EconomicMap <RequestedPattern , RuntimeConditionSet > requestedPatterns = ImageHeapMap .createNonLayeredMap ();
123
133
134
+ /**
135
+ * The string representation of {@link ModuleResourceKey} that are already registered in
136
+ * previous layers. Since the {@link ModuleResourceKey} contains a reference to a
137
+ * {@link Module}, the {@link Module} name is used instead of the object itself in the string
138
+ * representation. This works under the assumption that all modules have a different unique name
139
+ * in Layered Images. More details can be found in
140
+ * {@link Resources#getModuleResourceKeyString(ModuleResourceKey)}.
141
+ *
142
+ * The boolean associated to each {@link ModuleResourceKey} is true if the registered value is
143
+ * complete and false in the case of a negative query.
144
+ */
145
+ @ Platforms (Platform .HOSTED_ONLY .class ) //
146
+ private final Map <String , Boolean > previousLayerResources ;
147
+
148
+ /**
149
+ * The string representation of {@link RequestedPattern} that are already registered in previous
150
+ * layers.
151
+ */
152
+ @ Platforms (Platform .HOSTED_ONLY .class ) //
153
+ private final Set <String > previousLayerPatterns ;
154
+
124
155
public record RequestedPattern (String module , String resource ) {
125
156
}
126
157
@@ -155,6 +186,17 @@ public record ModuleResourceKey(Module module, String resource) {
155
186
private Function <Module , Module > hostedToRuntimeModuleMapper ;
156
187
157
188
Resources () {
189
+ this (Map .of (), Set .of ());
190
+ }
191
+
192
+ Resources (Map <String , Boolean > previousLayerResources , Set <String > previousLayerPatterns ) {
193
+ this .previousLayerResources = previousLayerResources ;
194
+ this .previousLayerPatterns = previousLayerPatterns ;
195
+ }
196
+
197
+ @ Platforms (Platform .HOSTED_ONLY .class )
198
+ public void setEncoder (SymbolEncoder encoder ) {
199
+ this .encoder = encoder ;
158
200
}
159
201
160
202
public GlobTrieNode <ConditionWithOrigin > getResourcesTrieRoot () {
@@ -240,6 +282,26 @@ private void updateTimeStamp() {
240
282
}
241
283
}
242
284
285
+ private static String getModuleResourceKeyString (ModuleResourceKey m ) {
286
+ /*
287
+ * A null module in the ModuleResourceKey represents any unnamed module, meaning that only
288
+ * one marker is needed for all of them and that if the module is not null, it is named (see
289
+ * Resources.createStorageKey). This string representation relies on the assumption that a
290
+ * layered image build cannot contain two modules with the same name, so Module#getName() is
291
+ * guaranteed to be unique for layered images.
292
+ */
293
+ String moduleName = m .module == null ? LayeredModuleSingleton .ALL_UNNAMED_MODULE_NAME : m .module .getName ();
294
+ return moduleName + m .resource ;
295
+ }
296
+
297
+ private void addResource (ModuleResourceKey key , ConditionalRuntimeValue <ResourceStorageEntryBase > entry ) {
298
+ String moduleResourceKeyString = getModuleResourceKeyString (key );
299
+ Boolean previousLayerData = previousLayerResources .get (moduleResourceKeyString );
300
+ if (previousLayerData == null || (!previousLayerData && entry .getValueUnconditionally () != NEGATIVE_QUERY_MARKER )) {
301
+ resources .put (key , entry );
302
+ }
303
+ }
304
+
243
305
@ Platforms (Platform .HOSTED_ONLY .class )
244
306
private void addEntry (Module module , String resourceName , boolean isDirectory , byte [] data , boolean fromJar , boolean isNegativeQuery ) {
245
307
VMError .guarantee (!BuildPhaseProvider .isAnalysisFinished (), "Trying to add a resource entry after analysis." );
@@ -250,15 +312,15 @@ private void addEntry(Module module, String resourceName, boolean isDirectory, b
250
312
ConditionalRuntimeValue <ResourceStorageEntryBase > entry = resources .get (key );
251
313
if (isNegativeQuery ) {
252
314
if (entry == null ) {
253
- resources . put (key , new ConditionalRuntimeValue <>(conditionSet , NEGATIVE_QUERY_MARKER ));
315
+ addResource (key , new ConditionalRuntimeValue <>(conditionSet , NEGATIVE_QUERY_MARKER ));
254
316
}
255
317
return ;
256
318
}
257
319
258
320
if (entry == null || entry .getValueUnconditionally () == NEGATIVE_QUERY_MARKER ) {
259
321
updateTimeStamp ();
260
322
entry = new ConditionalRuntimeValue <>(conditionSet , new ResourceStorageEntry (isDirectory , fromJar ));
261
- resources . put (key , entry );
323
+ addResource (key , entry );
262
324
} else {
263
325
if (key .module () != null ) {
264
326
// if the entry already exists, and it comes from a module, it is the same entry
@@ -307,7 +369,7 @@ public void registerIOException(Module module, String resourceName, IOException
307
369
ModuleResourceKey key = createStorageKey (module , resourceName );
308
370
synchronized (resources ) {
309
371
updateTimeStamp ();
310
- resources . put (key , new ConditionalRuntimeValue <>(RuntimeConditionSet .emptySet (), new ResourceExceptionEntry (e )));
372
+ addResource (key , new ConditionalRuntimeValue <>(RuntimeConditionSet .emptySet (), new ResourceExceptionEntry (e )));
311
373
}
312
374
}
313
375
@@ -326,7 +388,13 @@ public void registerIncludePattern(ConfigurationCondition condition, String modu
326
388
assert MissingRegistrationUtils .throwMissingRegistrationErrors ();
327
389
synchronized (requestedPatterns ) {
328
390
updateTimeStamp ();
329
- requestedPatterns .put (new RequestedPattern (encoder .encodeModule (module ), handleEscapedCharacters (pattern )), RuntimeConditionSet .createHosted (condition ));
391
+ addPattern (new RequestedPattern (encoder .encodeModule (module ), handleEscapedCharacters (pattern )), RuntimeConditionSet .createHosted (condition ));
392
+ }
393
+ }
394
+
395
+ private void addPattern (RequestedPattern pattern , RuntimeConditionSet condition ) {
396
+ if (!previousLayerPatterns .contains (pattern .toString ())) {
397
+ requestedPatterns .put (pattern , condition );
330
398
}
331
399
}
332
400
@@ -601,13 +669,65 @@ private static boolean matchResource(String pattern, String resource) {
601
669
public EnumSet <LayeredImageSingletonBuilderFlags > getImageBuilderFlags () {
602
670
return LayeredImageSingletonBuilderFlags .ALL_ACCESS ;
603
671
}
672
+
673
+ @ Override
674
+ public PersistFlags preparePersist (ImageSingletonWriter writer ) {
675
+ List <String > resourceKeys = new ArrayList <>();
676
+ List <Boolean > resourceRegistrationStates = new ArrayList <>();
677
+ Set <String > patterns = new HashSet <>(previousLayerPatterns );
678
+
679
+ var cursor = resources .getEntries ();
680
+ while (cursor .advance ()) {
681
+ resourceKeys .add (getModuleResourceKeyString (cursor .getKey ()));
682
+ boolean isNegativeQuery = cursor .getValue ().getValueUnconditionally () == NEGATIVE_QUERY_MARKER ;
683
+ resourceRegistrationStates .add (!isNegativeQuery );
684
+ }
685
+
686
+ for (var entry : previousLayerResources .entrySet ()) {
687
+ /*
688
+ * If a complete entry overwrites a negative query from a previous layer, the
689
+ * previousLayerResources map entry needs to be skipped to register the new entry for
690
+ * extension layers.
691
+ */
692
+ if (!resourceKeys .contains (entry .getKey ())) {
693
+ resourceKeys .add (entry .getKey ());
694
+ resourceRegistrationStates .add (entry .getValue ());
695
+ }
696
+ }
697
+
698
+ requestedPatterns .getKeys ().forEach (p -> patterns .add (p .toString ()));
699
+
700
+ writer .writeStringList (RESOURCE_KEYS , resourceKeys );
701
+ writer .writeBoolList (RESOURCE_REGISTRATION_STATES , resourceRegistrationStates );
702
+ writer .writeStringList (PATTERNS , patterns .stream ().toList ());
703
+
704
+ return PersistFlags .CREATE ;
705
+ }
706
+
707
+ @ SuppressWarnings ("unused" )
708
+ public static Object createFromLoader (ImageSingletonLoader loader ) {
709
+ List <String > previousLayerResourceKeys = loader .readStringList (RESOURCE_KEYS );
710
+ List <Boolean > previousLayerRegistrationStates = loader .readBoolList (RESOURCE_REGISTRATION_STATES );
711
+ Map <String , Boolean > previousLayerResources = new HashMap <>();
712
+
713
+ for (int i = 0 ; i < previousLayerResourceKeys .size (); ++i ) {
714
+ previousLayerResources .put (previousLayerResourceKeys .get (i ), previousLayerRegistrationStates .get (i ));
715
+ }
716
+
717
+ Set <String > previousLayerPatterns = Set .copyOf (loader .readStringList (PATTERNS ));
718
+
719
+ return new Resources (Collections .unmodifiableMap (previousLayerResources ), previousLayerPatterns );
720
+ }
604
721
}
605
722
606
723
@ AutomaticallyRegisteredFeature
607
724
final class ResourcesFeature implements InternalFeature {
608
725
@ Override
609
726
public void afterRegistration (AfterRegistrationAccess access ) {
610
- ImageSingletons .add (Resources .class , new Resources ());
727
+ if (ImageLayerBuildingSupport .firstImageBuild ()) {
728
+ ImageSingletons .add (Resources .class , new Resources ());
729
+ }
730
+ Resources .currentLayer ().setEncoder (SymbolEncoder .singleton ());
611
731
}
612
732
613
733
@ Override
0 commit comments