Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
*
* @author Stephane Nicoll
* @author Moritz Halbritter
* @author Mohan Krishna Namburu
*/
class IndexedJarStructure implements JarStructure {

Expand All @@ -54,21 +55,34 @@ class IndexedJarStructure implements JarStructure {
private static final Set<String> ENTRY_IGNORE_LIST = Set.of("META-INF/", "META-INF/MANIFEST.MF",
"META-INF/services/java.nio.file.spi.FileSystemProvider");

private static final String WAR_LIB_LOCATION = "WEB-INF/lib/";

private static final String WAR_LIB_PROVIDED_LOCATION = "WEB-INF/lib-provided/";

private final Manifest originalManifest;

private final String libLocation;
private final List<String> libLocations;

private final String classesLocation;

private final List<String> classpathEntries;

IndexedJarStructure(Manifest originalManifest, String indexFile) {
this.originalManifest = originalManifest;
this.libLocation = getLocation(originalManifest, "Spring-Boot-Lib");
this.libLocations = getLibLocations(originalManifest);
this.classesLocation = getLocation(originalManifest, "Spring-Boot-Classes");
this.classpathEntries = readIndexFile(indexFile);
}

private static List<String> getLibLocations(Manifest manifest) {
String libLocation = getLocation(manifest, "Spring-Boot-Lib");
if (WAR_LIB_LOCATION.equals(libLocation)) {
// An executable war also has its provided libraries on the classpath
return List.of(libLocation, WAR_LIB_PROVIDED_LOCATION);
}
return List.of(libLocation);
}

private static String getLocation(Manifest manifest, String attribute) {
String location = getMandatoryAttribute(manifest, attribute);
return (!location.endsWith("/")) ? location + "/" : location;
Expand Down Expand Up @@ -130,8 +144,12 @@ public Manifest createLauncherManifest(UnaryOperator<String> libraryTransformer)
}

private String toStructureDependency(String libEntryName) {
Assert.state(libEntryName.startsWith(this.libLocation), () -> "Invalid library location " + libEntryName);
return libEntryName.substring(this.libLocation.length());
for (String libLocation : this.libLocations) {
if (libEntryName.startsWith(libLocation)) {
return libEntryName.substring(libLocation.length());
}
}
throw new IllegalStateException("Invalid library location " + libEntryName);
}

private static String getMandatoryAttribute(Manifest manifest, String attribute) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,13 @@
import org.springframework.boot.jarmode.tools.JarStructure.Entry.Type;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;

/**
* Tests for {@link IndexedJarStructure}.
*
* @author Moritz Halbritter
* @author Mohan Krishna Namburu
*/
class IndexedJarStructureTests {

Expand Down Expand Up @@ -97,6 +99,64 @@ void shouldCreateLauncherManifest() throws IOException {
"Spring-Boot-Layers-Index");
}

@Test
void shouldResolveLibraryEntryOfWar() throws IOException {
IndexedJarStructure structure = createWarStructure();
Entry entry = structure.resolve("WEB-INF/lib/spring-web-6.1.4.jar");
assertThat(entry).isNotNull();
assertThat(entry.location()).isEqualTo("spring-web-6.1.4.jar");
assertThat(entry.originalLocation()).isEqualTo("WEB-INF/lib/spring-web-6.1.4.jar");
assertThat(entry.type()).isEqualTo(Type.LIBRARY);
}

@Test
void shouldResolveProvidedLibraryEntryOfWar() throws IOException {
IndexedJarStructure structure = createWarStructure();
Entry entry = structure.resolve("WEB-INF/lib-provided/tomcat-embed-core-10.1.19.jar");
assertThat(entry).isNotNull();
assertThat(entry.location()).isEqualTo("tomcat-embed-core-10.1.19.jar");
assertThat(entry.originalLocation()).isEqualTo("WEB-INF/lib-provided/tomcat-embed-core-10.1.19.jar");
assertThat(entry.type()).isEqualTo(Type.LIBRARY);
}

@Test
void shouldResolveApplicationEntryOfWar() throws IOException {
IndexedJarStructure structure = createWarStructure();
Entry entry = structure.resolve("WEB-INF/classes/application.properties");
assertThat(entry).isNotNull();
assertThat(entry.location()).isEqualTo("application.properties");
assertThat(entry.originalLocation()).isEqualTo("WEB-INF/classes/application.properties");
assertThat(entry.type()).isEqualTo(Type.APPLICATION_CLASS_OR_RESOURCE);
}

@Test
void shouldNotResolveNonExistingProvidedLibsOfWar() throws IOException {
IndexedJarStructure structure = createWarStructure();
Entry entry = structure.resolve("WEB-INF/lib-provided/doesnt-exists.jar");
assertThat(entry).isNull();
}

@Test
void shouldCreateLauncherManifestForWar() throws IOException {
IndexedJarStructure structure = createWarStructure();
Manifest manifest = structure.createLauncherManifest(UnaryOperator.identity());
Map<String, String> attributes = getAttributes(manifest);
assertThat(attributes).containsEntry("Class-Path",
"spring-web-6.1.4.jar spring-core-6.1.4.jar spring-boot-web-server-4.1.0.jar tomcat-embed-core-10.1.19.jar")
.containsEntry("Main-Class", "org.springframework.boot.jarmode.tools.IndexedJarStructureTests")
.doesNotContainKeys("Start-Class", "Spring-Boot-Classes", "Spring-Boot-Lib", "Spring-Boot-Classpath-Index",
"Spring-Boot-Layers-Index");
}

@Test
void shouldFailWhenLibraryIsOutsideOfKnownLocations() throws IOException {
IndexedJarStructure structure = new IndexedJarStructure(createManifest(), """
- "BOOT-INF/other/spring-web-6.1.4.jar"
""");
assertThatIllegalStateException().isThrownBy(() -> structure.createLauncherManifest(UnaryOperator.identity()))
.withMessage("Invalid library location BOOT-INF/other/spring-web-6.1.4.jar");
}

@Test
void shouldLoadFromFile(@TempDir File tempDir) throws IOException {
File jarFile = new File(tempDir, "test.jar");
Expand Down Expand Up @@ -157,6 +217,35 @@ private String createIndexFile() {
""";
}

private IndexedJarStructure createWarStructure() throws IOException {
return new IndexedJarStructure(createWarManifest(), createWarIndexFile());
}

private String createWarIndexFile() {
return """
- "WEB-INF/lib/spring-web-6.1.4.jar"
- "WEB-INF/lib/spring-core-6.1.4.jar"
- "WEB-INF/lib-provided/spring-boot-web-server-4.1.0.jar"
- "WEB-INF/lib-provided/tomcat-embed-core-10.1.19.jar"
""";
}

private Manifest createWarManifest() throws IOException {
return new Manifest(new ByteArrayInputStream("""
Manifest-Version: 1.0
Main-Class: org.springframework.boot.loader.launch.WarLauncher
Start-Class: org.springframework.boot.jarmode.tools.IndexedJarStructureTests
Spring-Boot-Version: 3.3.0-SNAPSHOT
Spring-Boot-Classes: WEB-INF/classes/
Spring-Boot-Lib: WEB-INF/lib/
Spring-Boot-Classpath-Index: WEB-INF/classpath.idx
Spring-Boot-Layers-Index: WEB-INF/layers.idx
Build-Jdk-Spec: 17
Implementation-Title: IndexedJarStructureTests
Implementation-Version: 0.0.1-SNAPSHOT
""".getBytes(StandardCharsets.UTF_8)));
}

private Manifest createManifest() throws IOException {
return new Manifest(new ByteArrayInputStream("""
Manifest-Version: 1.0
Expand Down