From d46e8598796ac614de7647c61201dd87005897db Mon Sep 17 00:00:00 2001 From: Arun Venmany Date: Tue, 2 Jun 2026 13:59:04 +0530 Subject: [PATCH 1/2] adding code changes to accept spring boot jars without release tag in name Signed-off-by: Arun Venmany --- .../common/plugins/util/SpringBootUtil.java | 6 +- .../plugins/util/SpringBootUtilTest.java | 230 ++++++++++++++++++ 2 files changed, 234 insertions(+), 2 deletions(-) create mode 100644 src/test/java/io/openliberty/tools/common/plugins/util/SpringBootUtilTest.java diff --git a/src/main/java/io/openliberty/tools/common/plugins/util/SpringBootUtil.java b/src/main/java/io/openliberty/tools/common/plugins/util/SpringBootUtil.java index d377d860d..3175bb860 100644 --- a/src/main/java/io/openliberty/tools/common/plugins/util/SpringBootUtil.java +++ b/src/main/java/io/openliberty/tools/common/plugins/util/SpringBootUtil.java @@ -28,8 +28,10 @@ public class SpringBootUtil { public static final String BOOT_VERSION_ATTRIBUTE = "Spring-Boot-Version"; public static final String BOOT_START_CLASS_ATTRIBUTE = "Start-Class"; - public static final String BOOT_JAR_EXPRESSION = "BOOT-INF/lib/spring-boot-\\d[\\.]\\d[\\.]\\d.RELEASE.jar"; - public static final String BOOT_WAR_EXPRESSION = "WEB-INF/lib/spring-boot-\\d[\\.]\\d[\\.]\\d.RELEASE.jar"; + // Updated regex to support both old (.RELEASE) and new versioning schemes (Spring Boot 3.0+, 4.0+) + // Matches: spring-boot-X.Y.Z.RELEASE.jar (old) or spring-boot-X.Y.Z.jar (new) + public static final String BOOT_JAR_EXPRESSION = "BOOT-INF/lib/spring-boot-\\d+[\\.]\\d+[\\.]\\d+(\\.RELEASE)?\\.jar"; + public static final String BOOT_WAR_EXPRESSION = "WEB-INF/lib/spring-boot-\\d+[\\.]\\d+[\\.]\\d+(\\.RELEASE)?\\.jar"; /** * Check whether the given artifact is a Spring Boot Uber JAR diff --git a/src/test/java/io/openliberty/tools/common/plugins/util/SpringBootUtilTest.java b/src/test/java/io/openliberty/tools/common/plugins/util/SpringBootUtilTest.java new file mode 100644 index 000000000..00a5b0ee8 --- /dev/null +++ b/src/test/java/io/openliberty/tools/common/plugins/util/SpringBootUtilTest.java @@ -0,0 +1,230 @@ +/** + * (C) Copyright IBM Corporation 2026. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.openliberty.tools.common.plugins.util; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import java.io.File; +import java.io.FileOutputStream; +import java.util.jar.Attributes; +import java.util.jar.JarEntry; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; + +import static org.junit.Assert.*; + +/** + * Unit tests for SpringBootUtil + * Tests both Tier 1 (manifest) and Tier 2 (regex) detection for Spring Boot applications + */ +public class SpringBootUtilTest { + + @Rule + public TemporaryFolder tempFolder = new TemporaryFolder(); + + // ========== Manifest Attributes based Detection Tests ========== + + @Test + public void testIsSpringBootUberJar_SpringBoot2_WithManifest() throws Exception { + File testJar = createSpringBootJarWithManifest("2.7.18", "BOOT-INF/lib/spring-boot-2.7.18.RELEASE.jar"); + assertTrue("Should detect Spring Boot 2.7.18 via manifest", + SpringBootUtil.isSpringBootUberJar(testJar)); + } + + @Test + public void testIsSpringBootUberJar_SpringBoot3_WithManifest() throws Exception { + File testJar = createSpringBootJarWithManifest("3.1.3", "BOOT-INF/lib/spring-boot-3.1.3.jar"); + assertTrue("Should detect Spring Boot 3.1.3 via manifest", + SpringBootUtil.isSpringBootUberJar(testJar)); + } + + @Test + public void testIsSpringBootUberJar_SpringBoot4_WithManifest() throws Exception { + File testJar = createSpringBootJarWithManifest("4.0.0", "BOOT-INF/lib/spring-boot-4.0.0.jar"); + assertTrue("Should detect Spring Boot 4.0.0 via manifest", + SpringBootUtil.isSpringBootUberJar(testJar)); + } + + @Test + public void testIsSpringBootUberJar_SpringBoot4_War_WithManifest() throws Exception { + File testWar = createSpringBootWarWithManifest("4.0.0", "WEB-INF/lib/spring-boot-4.0.0.jar"); + assertTrue("Should detect Spring Boot 4.0.0 WAR via manifest", + SpringBootUtil.isSpringBootUberJar(testWar)); + } + + // ========== Regex based Detection Tests ( Fallback) ========== + + @Test + public void testIsSpringBootUberJar_SpringBoot2_WithoutManifest() throws Exception { + File testJar = createSpringBootJarWithoutManifest("BOOT-INF/lib/spring-boot-2.7.18.RELEASE.jar"); + assertTrue("Should detect Spring Boot 2.7.18.RELEASE via regex", + SpringBootUtil.isSpringBootUberJar(testJar)); + } + + @Test + public void testIsSpringBootUberJar_SpringBoot3_WithoutManifest() throws Exception { + File testJar = createSpringBootJarWithoutManifest("BOOT-INF/lib/spring-boot-3.1.3.jar"); + assertTrue("Should detect Spring Boot 3.1.3 via regex", + SpringBootUtil.isSpringBootUberJar(testJar)); + } + + @Test + public void testIsSpringBootUberJar_SpringBoot4_WithoutManifest() throws Exception { + File testJar = createSpringBootJarWithoutManifest("BOOT-INF/lib/spring-boot-4.0.0.jar"); + assertTrue("Should detect Spring Boot 4.0.0 via regex - CRITICAL TEST", + SpringBootUtil.isSpringBootUberJar(testJar)); + } + + @Test + public void testIsSpringBootUberJar_SpringBoot3_4_WithoutManifest() throws Exception { + File testJar = createSpringBootJarWithoutManifest("BOOT-INF/lib/spring-boot-3.4.13.jar"); + assertTrue("Should detect Spring Boot 3.4.13 via regex", + SpringBootUtil.isSpringBootUberJar(testJar)); + } + + @Test + public void testIsSpringBootUberJar_SpringBoot4_War_WithoutManifest() throws Exception { + File testWar = createSpringBootJarWithoutManifest("WEB-INF/lib/spring-boot-4.0.0.jar"); + assertTrue("Should detect Spring Boot 4.0.0 WAR via regex", + SpringBootUtil.isSpringBootUberJar(testWar)); + } + + @Test + public void testIsSpringBootUberJar_MultiDigitVersion() throws Exception { + File testJar = createSpringBootJarWithoutManifest("BOOT-INF/lib/spring-boot-10.5.3.jar"); + assertTrue("Should detect Spring Boot 10.5.3 via regex - future-proof test", + SpringBootUtil.isSpringBootUberJar(testJar)); + } + + // ========== Negative Tests ========== + + @Test + public void testIsSpringBootUberJar_RegularJar() throws Exception { + File testJar = tempFolder.newFile("regular.jar"); + try (JarOutputStream jos = new JarOutputStream(new FileOutputStream(testJar))) { + Manifest manifest = new Manifest(); + manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0"); + jos.putNextEntry(new JarEntry("com/example/Main.class")); + jos.write(new byte[0]); + jos.closeEntry(); + } + assertFalse("Should NOT detect regular JAR as Spring Boot", + SpringBootUtil.isSpringBootUberJar(testJar)); + } + + @Test + public void testIsSpringBootUberJar_NullFile() { + assertFalse("Should return false for null file", + SpringBootUtil.isSpringBootUberJar(null)); + } + + @Test + public void testIsSpringBootUberJar_NonExistentFile() { + File nonExistent = new File("non-existent-file.jar"); + assertFalse("Should return false for non-existent file", + SpringBootUtil.isSpringBootUberJar(nonExistent)); + } + + /** + * Creates a Spring Boot JAR with manifest attributes + */ + private File createSpringBootJarWithManifest(String version, String springBootJarPath) throws Exception { + File testJar = tempFolder.newFile("spring-boot-" + version + "-test.jar"); + + try (JarOutputStream jos = new JarOutputStream(new FileOutputStream(testJar))) { + // Create manifest with Spring Boot attributes + Manifest manifest = new Manifest(); + manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0"); + manifest.getMainAttributes().putValue(SpringBootUtil.BOOT_VERSION_ATTRIBUTE, version); + manifest.getMainAttributes().putValue(SpringBootUtil.BOOT_START_CLASS_ATTRIBUTE, "com.example.Application"); + + // Write manifest + JarEntry manifestEntry = new JarEntry("META-INF/MANIFEST.MF"); + jos.putNextEntry(manifestEntry); + manifest.write(jos); + jos.closeEntry(); + + // Add spring-boot jar entry + JarEntry springBootEntry = new JarEntry(springBootJarPath); + jos.putNextEntry(springBootEntry); + jos.write(new byte[0]); + jos.closeEntry(); + } + + return testJar; + } + + /** + * Creates a Spring Boot WAR with manifest attributes + */ + private File createSpringBootWarWithManifest(String version, String springBootJarPath) throws Exception { + File testWar = tempFolder.newFile("spring-boot-" + version + "-test.war"); + + try (JarOutputStream jos = new JarOutputStream(new FileOutputStream(testWar))) { + // Create manifest with Spring Boot attributes + Manifest manifest = new Manifest(); + manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0"); + manifest.getMainAttributes().putValue(SpringBootUtil.BOOT_VERSION_ATTRIBUTE, version); + manifest.getMainAttributes().putValue(SpringBootUtil.BOOT_START_CLASS_ATTRIBUTE, "com.example.Application"); + + // Write manifest + JarEntry manifestEntry = new JarEntry("META-INF/MANIFEST.MF"); + jos.putNextEntry(manifestEntry); + manifest.write(jos); + jos.closeEntry(); + + // Add spring-boot jar entry + JarEntry springBootEntry = new JarEntry(springBootJarPath); + jos.putNextEntry(springBootEntry); + jos.write(new byte[0]); + jos.closeEntry(); + } + + return testWar; + } + + /** + * Creates a Spring Boot JAR WITHOUT manifest attributes + * This forces the regex-based detection to be used + */ + private File createSpringBootJarWithoutManifest(String springBootJarPath) throws Exception { + File testJar = tempFolder.newFile("spring-boot-no-manifest-test.jar"); + + try (JarOutputStream jos = new JarOutputStream(new FileOutputStream(testJar))) { + // Create manifest WITHOUT Spring Boot attributes + Manifest manifest = new Manifest(); + manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0"); + // NO Spring-Boot-Version attribute + // NO Start-Class attribute + + // Write manifest + JarEntry manifestEntry = new JarEntry("META-INF/MANIFEST.MF"); + jos.putNextEntry(manifestEntry); + manifest.write(jos); + jos.closeEntry(); + + // Add spring-boot jar entry - this is what regex will detect + JarEntry springBootEntry = new JarEntry(springBootJarPath); + jos.putNextEntry(springBootEntry); + jos.write(new byte[0]); + jos.closeEntry(); + } + + return testJar; + } +} \ No newline at end of file From 0b229107dbffdd61ad177ae0545f5e55ad59643f Mon Sep 17 00:00:00 2001 From: Arun Venmany Date: Wed, 3 Jun 2026 09:46:00 +0530 Subject: [PATCH 2/2] correcting copyright --- .../openliberty/tools/common/plugins/util/SpringBootUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/openliberty/tools/common/plugins/util/SpringBootUtil.java b/src/main/java/io/openliberty/tools/common/plugins/util/SpringBootUtil.java index 3175bb860..d76eaa4e3 100644 --- a/src/main/java/io/openliberty/tools/common/plugins/util/SpringBootUtil.java +++ b/src/main/java/io/openliberty/tools/common/plugins/util/SpringBootUtil.java @@ -1,5 +1,5 @@ /** - * (C) Copyright IBM Corporation 2018. + * (C) Copyright IBM Corporation 2018, 2026. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License.