Skip to content

Commit c4e43eb

Browse files
authored
Merge pull request #521 from venmanyarun/spring_boot_4_support
adding code changes to accept spring boot jars without release tag in…
2 parents af91702 + 0b22910 commit c4e43eb

2 files changed

Lines changed: 235 additions & 3 deletions

File tree

src/main/java/io/openliberty/tools/common/plugins/util/SpringBootUtil.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* (C) Copyright IBM Corporation 2018.
2+
* (C) Copyright IBM Corporation 2018, 2026.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -28,8 +28,10 @@ public class SpringBootUtil {
2828

2929
public static final String BOOT_VERSION_ATTRIBUTE = "Spring-Boot-Version";
3030
public static final String BOOT_START_CLASS_ATTRIBUTE = "Start-Class";
31-
public static final String BOOT_JAR_EXPRESSION = "BOOT-INF/lib/spring-boot-\\d[\\.]\\d[\\.]\\d.RELEASE.jar";
32-
public static final String BOOT_WAR_EXPRESSION = "WEB-INF/lib/spring-boot-\\d[\\.]\\d[\\.]\\d.RELEASE.jar";
31+
// Updated regex to support both old (.RELEASE) and new versioning schemes (Spring Boot 3.0+, 4.0+)
32+
// Matches: spring-boot-X.Y.Z.RELEASE.jar (old) or spring-boot-X.Y.Z.jar (new)
33+
public static final String BOOT_JAR_EXPRESSION = "BOOT-INF/lib/spring-boot-\\d+[\\.]\\d+[\\.]\\d+(\\.RELEASE)?\\.jar";
34+
public static final String BOOT_WAR_EXPRESSION = "WEB-INF/lib/spring-boot-\\d+[\\.]\\d+[\\.]\\d+(\\.RELEASE)?\\.jar";
3335

3436
/**
3537
* Check whether the given artifact is a Spring Boot Uber JAR
Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
/**
2+
* (C) Copyright IBM Corporation 2026.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.openliberty.tools.common.plugins.util;
17+
18+
import org.junit.Rule;
19+
import org.junit.Test;
20+
import org.junit.rules.TemporaryFolder;
21+
22+
import java.io.File;
23+
import java.io.FileOutputStream;
24+
import java.util.jar.Attributes;
25+
import java.util.jar.JarEntry;
26+
import java.util.jar.JarOutputStream;
27+
import java.util.jar.Manifest;
28+
29+
import static org.junit.Assert.*;
30+
31+
/**
32+
* Unit tests for SpringBootUtil
33+
* Tests both Tier 1 (manifest) and Tier 2 (regex) detection for Spring Boot applications
34+
*/
35+
public class SpringBootUtilTest {
36+
37+
@Rule
38+
public TemporaryFolder tempFolder = new TemporaryFolder();
39+
40+
// ========== Manifest Attributes based Detection Tests ==========
41+
42+
@Test
43+
public void testIsSpringBootUberJar_SpringBoot2_WithManifest() throws Exception {
44+
File testJar = createSpringBootJarWithManifest("2.7.18", "BOOT-INF/lib/spring-boot-2.7.18.RELEASE.jar");
45+
assertTrue("Should detect Spring Boot 2.7.18 via manifest",
46+
SpringBootUtil.isSpringBootUberJar(testJar));
47+
}
48+
49+
@Test
50+
public void testIsSpringBootUberJar_SpringBoot3_WithManifest() throws Exception {
51+
File testJar = createSpringBootJarWithManifest("3.1.3", "BOOT-INF/lib/spring-boot-3.1.3.jar");
52+
assertTrue("Should detect Spring Boot 3.1.3 via manifest",
53+
SpringBootUtil.isSpringBootUberJar(testJar));
54+
}
55+
56+
@Test
57+
public void testIsSpringBootUberJar_SpringBoot4_WithManifest() throws Exception {
58+
File testJar = createSpringBootJarWithManifest("4.0.0", "BOOT-INF/lib/spring-boot-4.0.0.jar");
59+
assertTrue("Should detect Spring Boot 4.0.0 via manifest",
60+
SpringBootUtil.isSpringBootUberJar(testJar));
61+
}
62+
63+
@Test
64+
public void testIsSpringBootUberJar_SpringBoot4_War_WithManifest() throws Exception {
65+
File testWar = createSpringBootWarWithManifest("4.0.0", "WEB-INF/lib/spring-boot-4.0.0.jar");
66+
assertTrue("Should detect Spring Boot 4.0.0 WAR via manifest",
67+
SpringBootUtil.isSpringBootUberJar(testWar));
68+
}
69+
70+
// ========== Regex based Detection Tests ( Fallback) ==========
71+
72+
@Test
73+
public void testIsSpringBootUberJar_SpringBoot2_WithoutManifest() throws Exception {
74+
File testJar = createSpringBootJarWithoutManifest("BOOT-INF/lib/spring-boot-2.7.18.RELEASE.jar");
75+
assertTrue("Should detect Spring Boot 2.7.18.RELEASE via regex",
76+
SpringBootUtil.isSpringBootUberJar(testJar));
77+
}
78+
79+
@Test
80+
public void testIsSpringBootUberJar_SpringBoot3_WithoutManifest() throws Exception {
81+
File testJar = createSpringBootJarWithoutManifest("BOOT-INF/lib/spring-boot-3.1.3.jar");
82+
assertTrue("Should detect Spring Boot 3.1.3 via regex",
83+
SpringBootUtil.isSpringBootUberJar(testJar));
84+
}
85+
86+
@Test
87+
public void testIsSpringBootUberJar_SpringBoot4_WithoutManifest() throws Exception {
88+
File testJar = createSpringBootJarWithoutManifest("BOOT-INF/lib/spring-boot-4.0.0.jar");
89+
assertTrue("Should detect Spring Boot 4.0.0 via regex - CRITICAL TEST",
90+
SpringBootUtil.isSpringBootUberJar(testJar));
91+
}
92+
93+
@Test
94+
public void testIsSpringBootUberJar_SpringBoot3_4_WithoutManifest() throws Exception {
95+
File testJar = createSpringBootJarWithoutManifest("BOOT-INF/lib/spring-boot-3.4.13.jar");
96+
assertTrue("Should detect Spring Boot 3.4.13 via regex",
97+
SpringBootUtil.isSpringBootUberJar(testJar));
98+
}
99+
100+
@Test
101+
public void testIsSpringBootUberJar_SpringBoot4_War_WithoutManifest() throws Exception {
102+
File testWar = createSpringBootJarWithoutManifest("WEB-INF/lib/spring-boot-4.0.0.jar");
103+
assertTrue("Should detect Spring Boot 4.0.0 WAR via regex",
104+
SpringBootUtil.isSpringBootUberJar(testWar));
105+
}
106+
107+
@Test
108+
public void testIsSpringBootUberJar_MultiDigitVersion() throws Exception {
109+
File testJar = createSpringBootJarWithoutManifest("BOOT-INF/lib/spring-boot-10.5.3.jar");
110+
assertTrue("Should detect Spring Boot 10.5.3 via regex - future-proof test",
111+
SpringBootUtil.isSpringBootUberJar(testJar));
112+
}
113+
114+
// ========== Negative Tests ==========
115+
116+
@Test
117+
public void testIsSpringBootUberJar_RegularJar() throws Exception {
118+
File testJar = tempFolder.newFile("regular.jar");
119+
try (JarOutputStream jos = new JarOutputStream(new FileOutputStream(testJar))) {
120+
Manifest manifest = new Manifest();
121+
manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
122+
jos.putNextEntry(new JarEntry("com/example/Main.class"));
123+
jos.write(new byte[0]);
124+
jos.closeEntry();
125+
}
126+
assertFalse("Should NOT detect regular JAR as Spring Boot",
127+
SpringBootUtil.isSpringBootUberJar(testJar));
128+
}
129+
130+
@Test
131+
public void testIsSpringBootUberJar_NullFile() {
132+
assertFalse("Should return false for null file",
133+
SpringBootUtil.isSpringBootUberJar(null));
134+
}
135+
136+
@Test
137+
public void testIsSpringBootUberJar_NonExistentFile() {
138+
File nonExistent = new File("non-existent-file.jar");
139+
assertFalse("Should return false for non-existent file",
140+
SpringBootUtil.isSpringBootUberJar(nonExistent));
141+
}
142+
143+
/**
144+
* Creates a Spring Boot JAR with manifest attributes
145+
*/
146+
private File createSpringBootJarWithManifest(String version, String springBootJarPath) throws Exception {
147+
File testJar = tempFolder.newFile("spring-boot-" + version + "-test.jar");
148+
149+
try (JarOutputStream jos = new JarOutputStream(new FileOutputStream(testJar))) {
150+
// Create manifest with Spring Boot attributes
151+
Manifest manifest = new Manifest();
152+
manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
153+
manifest.getMainAttributes().putValue(SpringBootUtil.BOOT_VERSION_ATTRIBUTE, version);
154+
manifest.getMainAttributes().putValue(SpringBootUtil.BOOT_START_CLASS_ATTRIBUTE, "com.example.Application");
155+
156+
// Write manifest
157+
JarEntry manifestEntry = new JarEntry("META-INF/MANIFEST.MF");
158+
jos.putNextEntry(manifestEntry);
159+
manifest.write(jos);
160+
jos.closeEntry();
161+
162+
// Add spring-boot jar entry
163+
JarEntry springBootEntry = new JarEntry(springBootJarPath);
164+
jos.putNextEntry(springBootEntry);
165+
jos.write(new byte[0]);
166+
jos.closeEntry();
167+
}
168+
169+
return testJar;
170+
}
171+
172+
/**
173+
* Creates a Spring Boot WAR with manifest attributes
174+
*/
175+
private File createSpringBootWarWithManifest(String version, String springBootJarPath) throws Exception {
176+
File testWar = tempFolder.newFile("spring-boot-" + version + "-test.war");
177+
178+
try (JarOutputStream jos = new JarOutputStream(new FileOutputStream(testWar))) {
179+
// Create manifest with Spring Boot attributes
180+
Manifest manifest = new Manifest();
181+
manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
182+
manifest.getMainAttributes().putValue(SpringBootUtil.BOOT_VERSION_ATTRIBUTE, version);
183+
manifest.getMainAttributes().putValue(SpringBootUtil.BOOT_START_CLASS_ATTRIBUTE, "com.example.Application");
184+
185+
// Write manifest
186+
JarEntry manifestEntry = new JarEntry("META-INF/MANIFEST.MF");
187+
jos.putNextEntry(manifestEntry);
188+
manifest.write(jos);
189+
jos.closeEntry();
190+
191+
// Add spring-boot jar entry
192+
JarEntry springBootEntry = new JarEntry(springBootJarPath);
193+
jos.putNextEntry(springBootEntry);
194+
jos.write(new byte[0]);
195+
jos.closeEntry();
196+
}
197+
198+
return testWar;
199+
}
200+
201+
/**
202+
* Creates a Spring Boot JAR WITHOUT manifest attributes
203+
* This forces the regex-based detection to be used
204+
*/
205+
private File createSpringBootJarWithoutManifest(String springBootJarPath) throws Exception {
206+
File testJar = tempFolder.newFile("spring-boot-no-manifest-test.jar");
207+
208+
try (JarOutputStream jos = new JarOutputStream(new FileOutputStream(testJar))) {
209+
// Create manifest WITHOUT Spring Boot attributes
210+
Manifest manifest = new Manifest();
211+
manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
212+
// NO Spring-Boot-Version attribute
213+
// NO Start-Class attribute
214+
215+
// Write manifest
216+
JarEntry manifestEntry = new JarEntry("META-INF/MANIFEST.MF");
217+
jos.putNextEntry(manifestEntry);
218+
manifest.write(jos);
219+
jos.closeEntry();
220+
221+
// Add spring-boot jar entry - this is what regex will detect
222+
JarEntry springBootEntry = new JarEntry(springBootJarPath);
223+
jos.putNextEntry(springBootEntry);
224+
jos.write(new byte[0]);
225+
jos.closeEntry();
226+
}
227+
228+
return testJar;
229+
}
230+
}

0 commit comments

Comments
 (0)