diff --git a/jib-cli/src/main/java/com/google/cloud/tools/jib/cli/jar/SpringBootExplodedProcessor.java b/jib-cli/src/main/java/com/google/cloud/tools/jib/cli/jar/SpringBootExplodedProcessor.java index 75c212be8d..2e6b93281a 100644 --- a/jib-cli/src/main/java/com/google/cloud/tools/jib/cli/jar/SpringBootExplodedProcessor.java +++ b/jib-cli/src/main/java/com/google/cloud/tools/jib/cli/jar/SpringBootExplodedProcessor.java @@ -35,6 +35,7 @@ import java.util.List; import java.util.Map; import java.util.function.Predicate; +import java.util.jar.Attributes; import java.util.jar.JarFile; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -135,14 +136,23 @@ public List createLayers() throws IOException { } @Override - public ImmutableList computeEntrypoint(List jvmFlags) { - ImmutableList.Builder entrypoint = ImmutableList.builder(); - entrypoint.add("java"); - entrypoint.addAll(jvmFlags); - entrypoint.add("-cp"); - entrypoint.add(JarLayers.APP_ROOT.toString()); - entrypoint.add("org.springframework.boot.loader.JarLauncher"); - return entrypoint.build(); + public ImmutableList computeEntrypoint(List jvmFlags) throws IOException { + try (JarFile jarFile = new JarFile(jarPath.toFile())) { + String mainClass = + jarFile.getManifest().getMainAttributes().getValue(Attributes.Name.MAIN_CLASS); + if (mainClass == null) { + throw new IllegalArgumentException( + "`Main-Class:` attribute for an application main class not defined in the input JAR's " + + "manifest (`META-INF/MANIFEST.MF` in the JAR)."); + } + ImmutableList.Builder entrypoint = ImmutableList.builder(); + entrypoint.add("java"); + entrypoint.addAll(jvmFlags); + entrypoint.add("-cp"); + entrypoint.add(JarLayers.APP_ROOT.toString()); + entrypoint.add(mainClass); + return entrypoint.build(); + } } @Override diff --git a/jib-cli/src/test/java/com/google/cloud/tools/jib/cli/jar/SpringBootExplodedProcessorTest.java b/jib-cli/src/test/java/com/google/cloud/tools/jib/cli/jar/SpringBootExplodedProcessorTest.java index 7440739fda..8b6495fa37 100644 --- a/jib-cli/src/test/java/com/google/cloud/tools/jib/cli/jar/SpringBootExplodedProcessorTest.java +++ b/jib-cli/src/test/java/com/google/cloud/tools/jib/cli/jar/SpringBootExplodedProcessorTest.java @@ -17,6 +17,7 @@ package com.google.cloud.tools.jib.cli.jar; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertThrows; import com.google.cloud.tools.jib.api.buildplan.AbsoluteUnixPath; import com.google.cloud.tools.jib.api.buildplan.FileEntriesLayer; @@ -43,6 +44,8 @@ public class SpringBootExplodedProcessorTest { private static final String SPRING_BOOT_LAYERED_WITH_ALL_EMPTY_LAYERS_LISTED = "jar/spring-boot/springboot_layered_allEmptyLayers.jar"; private static final String SPRING_BOOT_NOT_LAYERED = "jar/spring-boot/springboot_notLayered.jar"; + private static final String SPRING_BOOT_NO_MANIFEST = + "jar/spring-boot/springboot_noMainClass_in_manifest.jar"; private static final Integer JAR_JAVA_VERSION = 0; // any value @Rule public final TemporaryFolder temporaryFolder = new TemporaryFolder(); @@ -239,27 +242,53 @@ public void testCreateLayers_nonLayered() throws IOException, URISyntaxException } @Test - public void testComputeEntrypoint() { + public void testComputeEntrypoint() throws URISyntaxException, IOException { + Path sampleSpringBootJar = Paths.get(Resources.getResource(SPRING_BOOT_LAYERED).toURI()); SpringBootExplodedProcessor bootProcessor = new SpringBootExplodedProcessor( - Paths.get("ignored"), Paths.get("ignored"), JAR_JAVA_VERSION); + sampleSpringBootJar, Paths.get("ignored"), JAR_JAVA_VERSION); ImmutableList actualEntrypoint = bootProcessor.computeEntrypoint(new ArrayList<>()); assertThat(actualEntrypoint) .isEqualTo( - ImmutableList.of("java", "-cp", "/app", "org.springframework.boot.loader.JarLauncher")); + ImmutableList.of( + "java", "-cp", "/app", "org.springframework.boot.loader.launch.JarLauncher")); } @Test - public void testComputeEntrypoint_withJvmFlags() { + public void testComputeEntrypoint_withJvmFlags() throws URISyntaxException, IOException { + Path sampleSpringBootJar = Paths.get(Resources.getResource(SPRING_BOOT_LAYERED).toURI()); SpringBootExplodedProcessor bootProcessor = new SpringBootExplodedProcessor( - Paths.get("ignored"), Paths.get("ignored"), JAR_JAVA_VERSION); + sampleSpringBootJar, Paths.get("ignored"), JAR_JAVA_VERSION); ImmutableList actualEntrypoint = bootProcessor.computeEntrypoint(ImmutableList.of("-jvm-flag")); assertThat(actualEntrypoint) .isEqualTo( ImmutableList.of( - "java", "-jvm-flag", "-cp", "/app", "org.springframework.boot.loader.JarLauncher")); + "java", + "-jvm-flag", + "-cp", + "/app", + "org.springframework.boot.loader.launch.JarLauncher")); + } + + @Test + public void testComputeEntrypoint_noMainClass() throws URISyntaxException { + Path layeredSpringBootJar = Paths.get(Resources.getResource(SPRING_BOOT_NO_MANIFEST).toURI()); + SpringBootExplodedProcessor bootProcessor = + new SpringBootExplodedProcessor( + layeredSpringBootJar, Paths.get("ignored"), JAR_JAVA_VERSION); + + IllegalArgumentException exception = + assertThrows( + IllegalArgumentException.class, + () -> bootProcessor.computeEntrypoint(ImmutableList.of())); + + assertThat(exception) + .hasMessageThat() + .isEqualTo( + "`Main-Class:` attribute for an application main class not defined in the input JAR's manifest " + + "(`META-INF/MANIFEST.MF` in the JAR)."); } @Test diff --git a/jib-cli/src/test/resources/jar/spring-boot/springboot_layered.jar b/jib-cli/src/test/resources/jar/spring-boot/springboot_layered.jar index b12d52c097..ce7a47714d 100644 Binary files a/jib-cli/src/test/resources/jar/spring-boot/springboot_layered.jar and b/jib-cli/src/test/resources/jar/spring-boot/springboot_layered.jar differ diff --git a/jib-cli/src/test/resources/jar/spring-boot/springboot_layered_allEmptyLayers.jar b/jib-cli/src/test/resources/jar/spring-boot/springboot_layered_allEmptyLayers.jar index bf512abada..0a0ed111b0 100644 Binary files a/jib-cli/src/test/resources/jar/spring-boot/springboot_layered_allEmptyLayers.jar and b/jib-cli/src/test/resources/jar/spring-boot/springboot_layered_allEmptyLayers.jar differ diff --git a/jib-cli/src/test/resources/jar/spring-boot/springboot_layered_singleEmptyLayer.jar b/jib-cli/src/test/resources/jar/spring-boot/springboot_layered_singleEmptyLayer.jar index 345d6b2271..3cbbff093c 100644 Binary files a/jib-cli/src/test/resources/jar/spring-boot/springboot_layered_singleEmptyLayer.jar and b/jib-cli/src/test/resources/jar/spring-boot/springboot_layered_singleEmptyLayer.jar differ diff --git a/jib-cli/src/test/resources/jar/spring-boot/springboot_noMainClass_in_manifest.jar b/jib-cli/src/test/resources/jar/spring-boot/springboot_noMainClass_in_manifest.jar new file mode 100644 index 0000000000..a18d577eee Binary files /dev/null and b/jib-cli/src/test/resources/jar/spring-boot/springboot_noMainClass_in_manifest.jar differ diff --git a/jib-cli/src/test/resources/jar/spring-boot/springboot_notLayered.jar b/jib-cli/src/test/resources/jar/spring-boot/springboot_notLayered.jar index ce5ebcfd77..00ef082c5e 100644 Binary files a/jib-cli/src/test/resources/jar/spring-boot/springboot_notLayered.jar and b/jib-cli/src/test/resources/jar/spring-boot/springboot_notLayered.jar differ diff --git a/jib-cli/src/test/resources/jar/spring-boot/springboot_sample.jar b/jib-cli/src/test/resources/jar/spring-boot/springboot_sample.jar index 6c420cbd3c..08f5342f8b 100644 Binary files a/jib-cli/src/test/resources/jar/spring-boot/springboot_sample.jar and b/jib-cli/src/test/resources/jar/spring-boot/springboot_sample.jar differ