diff --git a/loader/spring-boot-jarmode-tools/src/main/java/org/springframework/boot/jarmode/tools/IndexedLayers.java b/loader/spring-boot-jarmode-tools/src/main/java/org/springframework/boot/jarmode/tools/IndexedLayers.java index 7f560d0656d..df2cc7eba84 100644 --- a/loader/spring-boot-jarmode-tools/src/main/java/org/springframework/boot/jarmode/tools/IndexedLayers.java +++ b/loader/spring-boot-jarmode-tools/src/main/java/org/springframework/boot/jarmode/tools/IndexedLayers.java @@ -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> layers = new LinkedHashMap<>(); + private final Map layerByFileName = new HashMap<>(); + + private final Map 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 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> 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 entry : this.layerByDirectoryName.entrySet()) { + if (name.startsWith(entry.getKey())) { + return entry.getValue(); } } throw new IllegalStateException("No layer defined in index for file '" + name + "'"); diff --git a/loader/spring-boot-jarmode-tools/src/test/java/org/springframework/boot/jarmode/tools/IndexedLayersTests.java b/loader/spring-boot-jarmode-tools/src/test/java/org/springframework/boot/jarmode/tools/IndexedLayersTests.java index 50053fb1a7b..f21d4444bbc 100644 --- a/loader/spring-boot-jarmode-tools/src/test/java/org/springframework/boot/jarmode/tools/IndexedLayersTests.java +++ b/loader/spring-boot-jarmode-tools/src/test/java/org/springframework/boot/jarmode/tools/IndexedLayersTests.java @@ -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());