Merge pull request #51653 from JunggiKim

Closes gh-51653

* speed-up-layer-lookup-when-extracting:
  Avoid scanning the whole layer index for every entry
This commit is contained in:
Stéphane Nicoll
2026-09-10 16:28:19 +02:00
2 changed files with 36 additions and 5 deletions
@@ -22,6 +22,7 @@ import java.nio.charset.StandardCharsets;
import java.nio.file.NoSuchFileException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
@@ -42,11 +43,16 @@ import org.springframework.util.StringUtils;
* @author Phillip Webb
* @author Madhura Bhave
* @author Moritz Halbritter
* @author Junggi Kim
*/
class IndexedLayers implements Layers {
private final Map<String, List<String>> layers = new LinkedHashMap<>();
private final Map<String, String> layerByFileName = new HashMap<>();
private final Map<String, String> layerByDirectoryName = new LinkedHashMap<>();
private final String indexFileLocation;
IndexedLayers(String indexFile, String indexFileLocation) {
@@ -70,6 +76,10 @@ class IndexedLayers implements Layers {
}
}
Assert.state(!this.layers.isEmpty(), "Empty layer index file loaded");
this.layers.forEach((layer, candidates) -> candidates.forEach((candidate) -> {
Map<String, String> index = candidate.endsWith("/") ? this.layerByDirectoryName : this.layerByFileName;
index.putIfAbsent(candidate, layer);
}));
}
@Override
@@ -84,11 +94,13 @@ class IndexedLayers implements Layers {
@Override
public String getLayer(String name) {
for (Map.Entry<String, List<String>> entry : this.layers.entrySet()) {
for (String candidate : entry.getValue()) {
if (candidate.equals(name) || (candidate.endsWith("/") && name.startsWith(candidate))) {
return entry.getKey();
}
String layer = this.layerByFileName.get(name);
if (layer != null) {
return layer;
}
for (Map.Entry<String, String> entry : this.layerByDirectoryName.entrySet()) {
if (name.startsWith(entry.getKey())) {
return entry.getValue();
}
}
throw new IllegalStateException("No layer defined in index for file '" + name + "'");
@@ -38,6 +38,7 @@ import static org.mockito.Mockito.mock;
*
* @author Phillip Webb
* @author Madhura Bhave
* @author Junggi Kim
*/
class IndexedLayersTests {
@@ -83,6 +84,13 @@ class IndexedLayersTests {
assertThat(layers.getLayer(mockEntry("META-INF/a/sub/directory/and/a/file"))).isEqualTo("application");
}
@Test
void getLayerWhenNameIsInMultipleLayersReturnsFirstLayer() {
IndexedLayers layers = new IndexedLayers(createIndexWithDuplicateName(), "BOOT-INF/classes");
assertThat(layers.getLayer(mockEntry("BOOT-INF/lib/a.jar"))).isEqualTo("first");
assertThat(layers.getLayer(mockEntry("META-INF/MANIFEST.MF"))).isEqualTo("first");
}
@Test
void getLayerWhenFileHasSpaceReturnsLayer() throws Exception {
IndexedLayers layers = new IndexedLayers(getIndex(), "BOOT-INF/classes");
@@ -102,6 +110,17 @@ class IndexedLayersTests {
return getFile("test-layers.idx");
}
private String createIndexWithDuplicateName() {
return """
- "first":
- "BOOT-INF/lib/a.jar"
- "META-INF/"
- "second":
- "BOOT-INF/lib/a.jar"
- "META-INF/"
""";
}
private String getFile(String fileName) throws Exception {
ClassPathResource resource = new ClassPathResource(fileName, getClass());
InputStreamReader reader = new InputStreamReader(resource.getInputStream());