From 7b4c062ebc529192efefc7b54400984b081dfe13 Mon Sep 17 00:00:00 2001 From: Stefan Prange Date: Sun, 28 Jan 2024 17:40:54 +0100 Subject: [PATCH] [issue #4175] SpringBootExplodedProcessor now uses JAR's Main-Class when creating the target image's entrypoint --- .../cli/jar/SpringBootExplodedProcessor.java | 26 +++++++---- .../jar/SpringBootExplodedProcessorTest.java | 41 +++++++++++++++--- .../jar/spring-boot/springboot_layered.jar | Bin 2719 -> 2753 bytes .../springboot_layered_allEmptyLayers.jar | Bin 2652 -> 2688 bytes .../springboot_layered_singleEmptyLayer.jar | Bin 2709 -> 2743 bytes .../springboot_noMainClass_in_manifest.jar | Bin 0 -> 813 bytes .../jar/spring-boot/springboot_notLayered.jar | Bin 2458 -> 2494 bytes .../jar/spring-boot/springboot_sample.jar | Bin 831 -> 865 bytes 8 files changed, 53 insertions(+), 14 deletions(-) create mode 100644 jib-cli/src/test/resources/jar/spring-boot/springboot_noMainClass_in_manifest.jar 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 b12d52c09779f657ddf7ab4b77e562086dccafb9..ce7a47714de5727c0781df7e58e2aea98d29ef7f 100644 GIT binary patch delta 404 zcmbO)dQg-lz?+$6vOcTSL^)Ma1`ZB}u(g&E*)xsSmM}0dGyt*46pPt|G<*jaBd%tl(U-y((@0)~7zpq<-Z5G2r ztCZF5_Zg;9y{|*?2~Vk#lRe=;czi$T#SoI$u;cz?+#xgn@&DgTXhWIgkMaCJGyfq6mOR85l$+Z)6n@`|Nw>w2!y0-bG$- zU9EFx&TkGfxMKX^X_20nua2kh#nM$QTr*xszL3<)_#v*U_I!z!#dC4dC*rEp7(}x_ z6@4sXWC-wP=lBwNXPFbwTo491Ve&avsm+XxoQ#}M%NQ6o3o}(SE5RIstU&~>2E>@W zfz^re;N;J&W=t;_CTp|VGKx$hbtQfCN{>ULdc|Hs4 tWPeUAM&8L)oW4xnER(%ilqSFCv}BqAVR~@sGu?(TXLBjAX|jSG2mqO^U>yJe 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 bf512abadaab636bbb5233336b234eb1891ae52e..0a0ed111b0acce6ff8f6a242b198518cd8aafc08 100644 GIT binary patch delta 399 zcmca3(jdwb;LXf3`3;NIL^;)Z1`ZB}fVGwp9~aNsR>8o)&n3|@FV`Uhk+z4n6RJu>D84L z?B@!n1^tZ-SoG}P)+-Uh3Nfjlet+jLyArc&UFmG$3ZF+!NudRQ|3zoB^{L!!KeF_T z-^PGQ!Oup`@}Gk4yj&Hfsp-$;_2smbB__+VnJ`sDn7)i$lS|pm7>`a~ z!{)`rz%=;>n>}OWWD9mHrZr5H?=eYE?q%0!d^UM6yC;(ZGgv^8!-6q+aukOlQ!Df2 zLKd;fi#fEIwu6|`lV1Uu??6nY$rhYiOgb!JW*es^W98%noIy-`KmzKMt+}MxxL85X F1OQc9h@Sue delta 382 zcmZn=y(1zV;LXe;!ob17!JysQ6374o6NL>#Q3SxE3=ATZ3s}V?KKq_I?c=Shcahgy zSL@uF^P7VVt{6XfTBPUYtK;c=v2+y+XT%H1Bhr$RS{XmYRn?v^(Xx0hF8V}VbsEFf z>`z4>ix?RKyxBR-#VQnBfF`>FF~|{3MC)aTpGA2&mz~RU=nR)U; yX0gc%EUc6HIJKBgLAU{&W=tF`P#K=dt2iy0LJ?ecE`6pM2yPUY0^2hdkfQ8>!>z?xJeUp&s_jQY}&0=_{wR!&=_Bkt;o-k#|2=u>8Gh_-JX3bv#)ImNlSSDDHveGEVphWC zT%e1=U`gZT7FH+5$CHn_A-fgRW2VW0%#xFjvFkI*GJ}}{9QusmlN~sM7-vnM%wfp1hk3F* zi`e8l99m58Kuqb$%A8t^s+0XVeVLM3fC5UB_j6h@ZGbTOSy?A5a%nMrhH&$_6xbYD HLCysL!0&<| delta 390 zcmdlkI#pCSz?+#xgn@&DgP~+XOCSRXOcXY#M-c#vGBAh$XRsgZ*3~+9=KSU$gDb`lo)+nO`RaK3UMyY3!ZqWCC; Ku*tK6oDKkB*J*VC 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 0000000000000000000000000000000000000000..a18d577eeedee43687676e70119a90c7094f965c GIT binary patch literal 813 zcmWIWW@Zs#;Nak3=r3{#WB>vt5CH_73@i-3t|5-Po_=on|4uP5Ff#;rvvYu?U`hhO znt{3*O4nLO{FV53+W{yG!XiKn*X--)=jrAe9HQsz_SyH$X&-N0y^Flwx?1PXoZlQ| zaK-q+(;_`DUmZ{1i>0erxMsYNd?Bfo@k3ly?fDWdi|68^PsCNHF^Fb=Dq;k>fSHAh zfdS}>W~g(47J&gLkaY6*4*@$D;xLe}T7k-nGk*8-GBPl*NH8!6AuH2!362jg$uCNM zJ7cfkVFMAj{nAY-okx`y$2Tm@PgvA&gvq&*VTxOm;#6(LZ!3b!?{zfJow|JCy8TWn z?3@u%7poV&aM^XezWc2-Ly+xO;R8$t9@BmpHa(mjc;WcQkIVXvV;)LPfBSfi^!x1h z9mVD=3O3i}wSUfcy0J!)yX^bqmpW&}oBcM;sa)}T8MklG-hJz*<)7Do{@&ZS2;Q7O_@~JqBn|#^#xm&|4vEVv*5I&w72!G%e9*f)-UkBbzxc=NIWm< z&?9GyPunU4fpY(|PIeU;$f(||_!nOj|6jZ7Q~$(G^R9{7aX0l;T-)`vdV4Ozyk{r$ z8C`!xR&=f3vDo&m`fFE*z_O$4pjbu@vH)*JCJ|ta;z}ft@Q0D`SO+B;m{wd#1g2?8 zqb-mL*NT*2Kqhc8;Er;b2@DMDflQ8o)&n3|@FV`Uhk+z4n6RJu>D84L z?B@!n1^tZ-SoG}P)+-Uh3Nfjlet+jLyArc&UFmG$3ZF+!NudRQ|3zoB^{L!!KeF_T z-^PGQ!Oup`@}Gk4yj&Hfsp-$;_2hAMBCE*c2v#jdTOdn$@-iT+8pw)dJPITwC%dp2GBGesE@!i3)SkSN&66<| zNJ>o>W!Gn%4`yv(5}2INuEG9@iGd-%D1Gt+cG=0h+4&e*CNJa=pZt(ri_r|ol9{Z* lp~aXBWGPQB1F}{FSuu=1z+0sy`Mg|h$v delta 402 zcmdldJWE(Oz?+#xgn@&DgTZ5ZQy>EfOcXW{MG*jtGBAite$OHv@!9vxX&-N0y^Flw zx?1PXoZlQ|aK-q+(;_`DUmZ{1i>0erI3r$29+8%m)XMlFuB!HYiI&B4anUE@s?!** zW`8RBSj5N>;LXlqE>@x70yNnbh(V5+%)=_RnU(Q4vl7g1WECP%H$bS#?^v7|4^K8^ zHDi1=`2nNIfY|g~<-=8cfv?nJY{JKpCmYYuWi2_f6izZpie2 zX|f@Q*ko1?Ek>cq1{`jTo>sWXvuj1rmOJxE13;@k4YRmut 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 6c420cbd3ca3f0c433becc725385ff6ba6c803b3..08f5342f8be1dc9ad7c8e3a796c58f50fabc4a40 100644 GIT binary patch delta 249 zcmdnb_K=Mwz?+$6vNe;`L^;)Z1`ZB};5C*J*)xsSmM}0dGyt&(5c|4@IO=-(x#{~l z`gyv!28ZbRy2+l}$aTm-#O18+WUiK%$T?sCyO$xu!S>$qyRmw2!y0-bG$- zU9EFx&TkGfxMKX^X_20nua2kh#nM$QTr*xszL3<)_#v*U_I!z!#dC4dC*rEp7(}x_ z6@4sXWC-wP=lBwNXPFbwTo491Ve%~|smY9t;#^R>fYfFs#tKFym_v{?h#(XJS(EoL YIWZob{GZ8;=>@}Nb7mDbQznoC02MhnQ2+n{