Relocate launcher classes

Create alternative launcher classes under the package
`org.springframework.boot.loader.launch` and use them in favor
of the previous location.

This update is designed to improve compatibility with future
changes in the loader.

Closes gh-37667
pull/37640/head
Phillip Webb 1 year ago
parent f947bad3f7
commit c22548982a

@ -44,7 +44,7 @@ COPY --from=builder application/dependencies/ ./
COPY --from=builder application/spring-boot-loader/ ./ COPY --from=builder application/spring-boot-loader/ ./
COPY --from=builder application/snapshot-dependencies/ ./ COPY --from=builder application/snapshot-dependencies/ ./
COPY --from=builder application/application/ ./ COPY --from=builder application/application/ ./
ENTRYPOINT ["java", "org.springframework.boot.loader.JarLauncher"] ENTRYPOINT ["java", "org.springframework.boot.loader.launch.JarLauncher"]
---- ----
Assuming the above `Dockerfile` is in the current directory, your docker image can be built with `docker build .`, or optionally specifying the path to your application jar, as shown in the following example: Assuming the above `Dockerfile` is in the current directory, your docker image can be built with `docker build .`, or optionally specifying the path to your application jar, as shown in the following example:

@ -28,8 +28,8 @@ The following shows an example of a `layers.idx` file:
- BOOT-INF/lib/library1.jar - BOOT-INF/lib/library1.jar
- BOOT-INF/lib/library2.jar - BOOT-INF/lib/library2.jar
- "spring-boot-loader": - "spring-boot-loader":
- org/springframework/boot/loader/JarLauncher.class - org/springframework/boot/loader/launch/JarLauncher.class
- org/springframework/boot/loader/jar/JarEntry.class - ... <other classes>
- "snapshot-dependencies": - "snapshot-dependencies":
- BOOT-INF/lib/library3-SNAPSHOT.jar - BOOT-INF/lib/library3-SNAPSHOT.jar
- "application": - "application":

@ -13,7 +13,7 @@ One way to run an unpacked archive is by starting the appropriate launcher, as f
[source,shell,indent=0,subs="verbatim"] [source,shell,indent=0,subs="verbatim"]
---- ----
$ jar -xf myapp.jar $ jar -xf myapp.jar
$ java org.springframework.boot.loader.JarLauncher $ java org.springframework.boot.loader.launch.JarLauncher
---- ----
This is actually slightly faster on startup (depending on the size of the jar) than running from an unexploded archive. This is actually slightly faster on startup (depending on the size of the jar) than running from an unexploded archive.

@ -22,7 +22,7 @@ The following example shows a typical `MANIFEST.MF` for an executable jar file:
[indent=0] [indent=0]
---- ----
Main-Class: org.springframework.boot.loader.JarLauncher Main-Class: org.springframework.boot.loader.launch.JarLauncher
Start-Class: com.mycompany.project.MyApplication Start-Class: com.mycompany.project.MyApplication
---- ----

@ -132,7 +132,7 @@ The printed banner is registered as a singleton bean under the following name: `
The `${application.version}` and `${application.formatted-version}` properties are only available if you are using Spring Boot launchers. The `${application.version}` and `${application.formatted-version}` properties are only available if you are using Spring Boot launchers.
The values will not be resolved if you are running an unpacked jar and starting it with `java -cp <classpath> <mainclass>`. The values will not be resolved if you are running an unpacked jar and starting it with `java -cp <classpath> <mainclass>`.
This is why we recommend that you always launch unpacked jars using `java org.springframework.boot.loader.JarLauncher`. This is why we recommend that you always launch unpacked jars using `java org.springframework.boot.loader.launch.JarLauncher`.
This will initialize the `application.*` banner variables before building the classpath and launching your app. This will initialize the `application.*` banner variables before building the classpath and launching your app.
==== ====

@ -290,7 +290,7 @@ The following example shows how to build an executable archive with Ant:
</mappedresources> </mappedresources>
<zipfileset src="${lib.dir}/loader/spring-boot-loader-jar-${spring-boot.version}.jar" /> <zipfileset src="${lib.dir}/loader/spring-boot-loader-jar-${spring-boot.version}.jar" />
<manifest> <manifest>
<attribute name="Main-Class" value="org.springframework.boot.loader.JarLauncher" /> <attribute name="Main-Class" value="org.springframework.boot.loader.launch.JarLauncher" />
<attribute name="Start-Class" value="${start-class}" /> <attribute name="Start-Class" value="${start-class}" />
</manifest> </manifest>
</jar> </jar>

@ -61,7 +61,7 @@
<zipfileset src="${destdir}/dependency/spring-boot-loader.jar" /> <zipfileset src="${destdir}/dependency/spring-boot-loader.jar" />
<manifest> <manifest>
<attribute name="Main-Class" <attribute name="Main-Class"
value="org.springframework.boot.loader.JarLauncher" /> value="org.springframework.boot.loader.launch.JarLauncher" />
<attribute name="Start-Class" value="${start-class}" /> <attribute name="Start-Class" value="${start-class}" />
<attribute name="Spring-Boot-Classes" value="BOOT-INF/classes/" /> <attribute name="Spring-Boot-Classes" value="BOOT-INF/classes/" />
<attribute name="Spring-Boot-Lib" value="BOOT-INF/lib/" /> <attribute name="Spring-Boot-Lib" value="BOOT-INF/lib/" />

@ -66,7 +66,7 @@ task fullJar(type: Jar) {
} }
manifest { manifest {
attributes( attributes(
"Main-Class": "org.springframework.boot.loader.JarLauncher", "Main-Class": "org.springframework.boot.loader.launch.JarLauncher",
"Start-Class": "org.springframework.boot.cli.SpringCli" "Start-Class": "org.springframework.boot.cli.SpringCli"
) )
} }

@ -59,7 +59,7 @@ set CMD_LINE_ARGS=%$
@rem Setup the command line @rem Setup the command line
set CLASSPATH=%SPRING_HOME%\lib\* set CLASSPATH=%SPRING_HOME%\lib\*
"%JAVA_EXE%" %JAVA_OPTS% -cp "%CLASSPATH%" org.springframework.boot.loader.JarLauncher %CMD_LINE_ARGS% "%JAVA_EXE%" %JAVA_OPTS% -cp "%CLASSPATH%" org.springframework.boot.loader.launch.JarLauncher %CMD_LINE_ARGS%
:end :end
@rem End local scope for the variables with windows NT shell @rem End local scope for the variables with windows NT shell

@ -115,4 +115,4 @@ if $cygwin; then
fi fi
IFS=" " read -r -a javaOpts <<< "$JAVA_OPTS" IFS=" " read -r -a javaOpts <<< "$JAVA_OPTS"
exec "${JAVA_HOME}/bin/java" "${javaOpts[@]}" -cp "$CLASSPATH" org.springframework.boot.loader.JarLauncher "$@" exec "${JAVA_HOME}/bin/java" "${javaOpts[@]}" -cp "$CLASSPATH" org.springframework.boot.loader.launch.JarLauncher "$@"

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2022 the original author or authors. * Copyright 2012-2023 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -33,7 +33,7 @@ import org.springframework.boot.loader.tools.JavaExecutable;
*/ */
class ForkProcessCommand extends RunProcessCommand { class ForkProcessCommand extends RunProcessCommand {
private static final String MAIN_CLASS = "org.springframework.boot.loader.JarLauncher"; private static final String MAIN_CLASS = "org.springframework.boot.loader.launch.JarLauncher";
private final Command command; private final Command command;

@ -10,7 +10,7 @@ tasks.named("bootWar") {
// tag::properties-launcher[] // tag::properties-launcher[]
tasks.named("bootWar") { tasks.named("bootWar") {
manifest { manifest {
attributes 'Main-Class': 'org.springframework.boot.loader.PropertiesLauncher' attributes 'Main-Class': 'org.springframework.boot.loader.launch.PropertiesLauncher'
} }
} }
// end::properties-launcher[] // end::properties-launcher[]

@ -12,7 +12,7 @@ tasks.named<BootWar>("bootWar") {
// tag::properties-launcher[] // tag::properties-launcher[]
tasks.named<BootWar>("bootWar") { tasks.named<BootWar>("bootWar") {
manifest { manifest {
attributes("Main-Class" to "org.springframework.boot.loader.PropertiesLauncher") attributes("Main-Class" to "org.springframework.boot.loader.launch.PropertiesLauncher")
} }
} }
// end::properties-launcher[] // end::properties-launcher[]

@ -61,9 +61,9 @@ class BootArchiveSupport {
static { static {
Set<String> defaultLauncherClasses = new HashSet<>(); Set<String> defaultLauncherClasses = new HashSet<>();
defaultLauncherClasses.add("org.springframework.boot.loader.JarLauncher"); defaultLauncherClasses.add("org.springframework.boot.loader.launch.JarLauncher");
defaultLauncherClasses.add("org.springframework.boot.loader.PropertiesLauncher"); defaultLauncherClasses.add("org.springframework.boot.loader.launch.PropertiesLauncher");
defaultLauncherClasses.add("org.springframework.boot.loader.WarLauncher"); defaultLauncherClasses.add("org.springframework.boot.loader.launch.WarLauncher");
DEFAULT_LAUNCHER_CLASSES = Collections.unmodifiableSet(defaultLauncherClasses); DEFAULT_LAUNCHER_CLASSES = Collections.unmodifiableSet(defaultLauncherClasses);
} }

@ -49,7 +49,7 @@ import org.gradle.work.DisableCachingByDefault;
@DisableCachingByDefault(because = "Not worth caching") @DisableCachingByDefault(because = "Not worth caching")
public abstract class BootJar extends Jar implements BootArchive { public abstract class BootJar extends Jar implements BootArchive {
private static final String LAUNCHER = "org.springframework.boot.loader.JarLauncher"; private static final String LAUNCHER = "org.springframework.boot.loader.launch.JarLauncher";
private static final String CLASSES_DIRECTORY = "BOOT-INF/classes/"; private static final String CLASSES_DIRECTORY = "BOOT-INF/classes/";

@ -48,7 +48,7 @@ import org.gradle.work.DisableCachingByDefault;
@DisableCachingByDefault(because = "Not worth caching") @DisableCachingByDefault(because = "Not worth caching")
public abstract class BootWar extends War implements BootArchive { public abstract class BootWar extends War implements BootArchive {
private static final String LAUNCHER = "org.springframework.boot.loader.WarLauncher"; private static final String LAUNCHER = "org.springframework.boot.loader.launch.WarLauncher";
private static final String CLASSES_DIRECTORY = "WEB-INF/classes/"; private static final String CLASSES_DIRECTORY = "WEB-INF/classes/";

@ -166,7 +166,7 @@ class PackagingDocumentationTests {
assertThat(file).isFile(); assertThat(file).isFile();
try (JarFile jar = new JarFile(file)) { try (JarFile jar = new JarFile(file)) {
assertThat(jar.getManifest().getMainAttributes().getValue("Main-Class")) assertThat(jar.getManifest().getMainAttributes().getValue("Main-Class"))
.isEqualTo("org.springframework.boot.loader.PropertiesLauncher"); .isEqualTo("org.springframework.boot.loader.launch.PropertiesLauncher");
} }
} }

@ -270,7 +270,9 @@ abstract class AbstractBootArchiveTests<T extends Jar & BootArchive> {
void loaderIsWrittenToTheRootOfTheJarWhenUsingThePropertiesLauncher() throws IOException { void loaderIsWrittenToTheRootOfTheJarWhenUsingThePropertiesLauncher() throws IOException {
this.task.getMainClass().set("com.example.Main"); this.task.getMainClass().set("com.example.Main");
executeTask(); executeTask();
this.task.getManifest().getAttributes().put("Main-Class", "org.springframework.boot.loader.PropertiesLauncher"); this.task.getManifest()
.getAttributes()
.put("Main-Class", "org.springframework.boot.loader.launch.PropertiesLauncher");
try (JarFile jarFile = new JarFile(this.task.getArchiveFile().get().getAsFile())) { try (JarFile jarFile = new JarFile(this.task.getArchiveFile().get().getAsFile())) {
assertThat(jarFile.getEntry("org/springframework/boot/loader/LaunchedURLClassLoader.class")).isNotNull(); assertThat(jarFile.getEntry("org/springframework/boot/loader/LaunchedURLClassLoader.class")).isNotNull();
assertThat(jarFile.getEntry("org/springframework/boot/loader/")).isNotNull(); assertThat(jarFile.getEntry("org/springframework/boot/loader/")).isNotNull();
@ -362,7 +364,7 @@ abstract class AbstractBootArchiveTests<T extends Jar & BootArchive> {
assertThat(jarFile.getManifest().getMainAttributes().getValue("Main-Class")) assertThat(jarFile.getManifest().getMainAttributes().getValue("Main-Class"))
.isEqualTo("com.example.CustomLauncher"); .isEqualTo("com.example.CustomLauncher");
assertThat(jarFile.getManifest().getMainAttributes().getValue("Start-Class")).isEqualTo("com.example.Main"); assertThat(jarFile.getManifest().getMainAttributes().getValue("Start-Class")).isEqualTo("com.example.Main");
assertThat(jarFile.getEntry("org/springframework/boot/loader/LaunchedURLClassLoader.class")).isNull(); assertThat(jarFile.getEntry("org/springframework/boot/loader/launch/LaunchedClassLoader.class")).isNull();
} }
} }

@ -41,7 +41,7 @@ import static org.assertj.core.api.Assertions.assertThat;
class BootJarTests extends AbstractBootArchiveTests<BootJar> { class BootJarTests extends AbstractBootArchiveTests<BootJar> {
BootJarTests() { BootJarTests() {
super(BootJar.class, "org.springframework.boot.loader.JarLauncher", "BOOT-INF/lib/", "BOOT-INF/classes/", super(BootJar.class, "org.springframework.boot.loader.launch.JarLauncher", "BOOT-INF/lib/", "BOOT-INF/classes/",
"BOOT-INF/"); "BOOT-INF/");
} }

@ -39,7 +39,7 @@ import static org.assertj.core.api.Assertions.assertThat;
class BootWarTests extends AbstractBootArchiveTests<BootWar> { class BootWarTests extends AbstractBootArchiveTests<BootWar> {
BootWarTests() { BootWarTests() {
super(BootWar.class, "org.springframework.boot.loader.WarLauncher", "WEB-INF/lib/", "WEB-INF/classes/", super(BootWar.class, "org.springframework.boot.loader.launch.WarLauncher", "WEB-INF/lib/", "WEB-INF/classes/",
"WEB-INF/"); "WEB-INF/");
} }

@ -21,5 +21,5 @@ task explode(type: Sync) {
task launch(type: JavaExec) { task launch(type: JavaExec) {
classpath = files(explode) classpath = files(explode)
mainClass = 'org.springframework.boot.loader.JarLauncher' mainClass = 'org.springframework.boot.loader.launch.JarLauncher'
} }

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2021 the original author or authors. * Copyright 2012-2023 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -66,7 +66,7 @@ public final class Layouts {
@Override @Override
public String getLauncherClassName() { public String getLauncherClassName() {
return "org.springframework.boot.loader.JarLauncher"; return "org.springframework.boot.loader.launch.JarLauncher";
} }
@Override @Override
@ -108,7 +108,7 @@ public final class Layouts {
@Override @Override
public String getLauncherClassName() { public String getLauncherClassName() {
return "org.springframework.boot.loader.PropertiesLauncher"; return "org.springframework.boot.loader.launch.PropertiesLauncher";
} }
} }
@ -148,7 +148,7 @@ public final class Layouts {
@Override @Override
public String getLauncherClassName() { public String getLauncherClassName() {
return "org.springframework.boot.loader.WarLauncher"; return "org.springframework.boot.loader.launch.WarLauncher";
} }
@Override @Override

@ -105,7 +105,7 @@ abstract class AbstractPackagerTests<P extends Packager> {
execute(packager, NO_LIBRARIES); execute(packager, NO_LIBRARIES);
Manifest actualManifest = getPackagedManifest(); Manifest actualManifest = getPackagedManifest();
assertThat(actualManifest.getMainAttributes().getValue("Main-Class")) assertThat(actualManifest.getMainAttributes().getValue("Main-Class"))
.isEqualTo("org.springframework.boot.loader.JarLauncher"); .isEqualTo("org.springframework.boot.loader.launch.JarLauncher");
assertThat(actualManifest.getMainAttributes().getValue("Start-Class")).isEqualTo("a.b.C"); assertThat(actualManifest.getMainAttributes().getValue("Start-Class")).isEqualTo("a.b.C");
assertThat(hasPackagedLauncherClasses()).isTrue(); assertThat(hasPackagedLauncherClasses()).isTrue();
} }
@ -121,7 +121,7 @@ abstract class AbstractPackagerTests<P extends Packager> {
execute(packager, NO_LIBRARIES); execute(packager, NO_LIBRARIES);
Manifest actualManifest = getPackagedManifest(); Manifest actualManifest = getPackagedManifest();
assertThat(actualManifest.getMainAttributes().getValue("Main-Class")) assertThat(actualManifest.getMainAttributes().getValue("Main-Class"))
.isEqualTo("org.springframework.boot.loader.JarLauncher"); .isEqualTo("org.springframework.boot.loader.launch.JarLauncher");
assertThat(actualManifest.getMainAttributes().getValue("Start-Class")).isEqualTo("a.b.C"); assertThat(actualManifest.getMainAttributes().getValue("Start-Class")).isEqualTo("a.b.C");
assertThat(hasPackagedLauncherClasses()).isTrue(); assertThat(hasPackagedLauncherClasses()).isTrue();
} }
@ -133,7 +133,7 @@ abstract class AbstractPackagerTests<P extends Packager> {
execute(packager, NO_LIBRARIES); execute(packager, NO_LIBRARIES);
Manifest actualManifest = getPackagedManifest(); Manifest actualManifest = getPackagedManifest();
assertThat(actualManifest.getMainAttributes().getValue("Main-Class")) assertThat(actualManifest.getMainAttributes().getValue("Main-Class"))
.isEqualTo("org.springframework.boot.loader.JarLauncher"); .isEqualTo("org.springframework.boot.loader.launch.JarLauncher");
assertThat(actualManifest.getMainAttributes().getValue("Start-Class")).isEqualTo("a.b.C"); assertThat(actualManifest.getMainAttributes().getValue("Start-Class")).isEqualTo("a.b.C");
assertThat(hasPackagedLauncherClasses()).isTrue(); assertThat(hasPackagedLauncherClasses()).isTrue();
} }
@ -684,7 +684,7 @@ abstract class AbstractPackagerTests<P extends Packager> {
protected boolean hasPackagedLauncherClasses() throws IOException { protected boolean hasPackagedLauncherClasses() throws IOException {
return hasPackagedEntry("org/springframework/boot/") return hasPackagedEntry("org/springframework/boot/")
&& hasPackagedEntry("org/springframework/boot/loader/JarLauncher.class"); && hasPackagedEntry("org/springframework/boot/loader/launch/JarLauncher.class");
} }
private boolean hasPackagedEntry(String name) throws IOException { private boolean hasPackagedEntry(String name) throws IOException {

@ -79,7 +79,7 @@ class RepackagerTests extends AbstractPackagerTests<Repackager> {
repackager.repackage(NO_LIBRARIES); repackager.repackage(NO_LIBRARIES);
Manifest actualManifest = getPackagedManifest(); Manifest actualManifest = getPackagedManifest();
assertThat(actualManifest.getMainAttributes().getValue("Main-Class")) assertThat(actualManifest.getMainAttributes().getValue("Main-Class"))
.isEqualTo("org.springframework.boot.loader.JarLauncher"); .isEqualTo("org.springframework.boot.loader.launch.JarLauncher");
assertThat(actualManifest.getMainAttributes().getValue("Start-Class")).isEqualTo("a.b.C"); assertThat(actualManifest.getMainAttributes().getValue("Start-Class")).isEqualTo("a.b.C");
assertThat(hasPackagedLauncherClasses()).isTrue(); assertThat(hasPackagedLauncherClasses()).isTrue();
} }
@ -220,7 +220,7 @@ class RepackagerTests extends AbstractPackagerTests<Repackager> {
private boolean hasLauncherClasses(File file) throws IOException { private boolean hasLauncherClasses(File file) throws IOException {
return hasEntry(file, "org/springframework/boot/") return hasEntry(file, "org/springframework/boot/")
&& hasEntry(file, "org/springframework/boot/loader/JarLauncher.class"); && hasEntry(file, "org/springframework/boot/loader/launch/JarLauncher.class");
} }
private boolean hasEntry(File file, String name) throws IOException { private boolean hasEntry(File file, String name) throws IOException {

@ -0,0 +1,34 @@
/*
* Copyright 2012-2023 the original author or authors.
*
* 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
*
* https://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 org.springframework.boot.loader.launch;
/**
* Repackaged {@link org.springframework.boot.loader.JarLauncher}.
*
* @author Phillip Webb
* @since 3.2.0
*/
public final class JarLauncher {
private JarLauncher() {
}
public static void main(String[] args) throws Exception {
org.springframework.boot.loader.JarLauncher.main(args);
}
}

@ -0,0 +1,34 @@
/*
* Copyright 2012-2023 the original author or authors.
*
* 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
*
* https://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 org.springframework.boot.loader.launch;
/**
* Repackaged {@link org.springframework.boot.loader.PropertiesLauncher}.
*
* @author Phillip Webb
* @since 3.2.0
*/
public final class PropertiesLauncher {
private PropertiesLauncher() {
}
public static void main(String[] args) throws Exception {
org.springframework.boot.loader.PropertiesLauncher.main(args);
}
}

@ -0,0 +1,34 @@
/*
* Copyright 2012-2023 the original author or authors.
*
* 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
*
* https://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 org.springframework.boot.loader.launch;
/**
* Repackaged {@link org.springframework.boot.loader.WarLauncher}.
*
* @author Phillip Webb
* @since 3.2.0
*/
public final class WarLauncher {
private WarLauncher() {
}
public static void main(String[] args) throws Exception {
org.springframework.boot.loader.WarLauncher.main(args);
}
}

@ -0,0 +1,23 @@
/*
* Copyright 2012-2023 the original author or authors.
*
* 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
*
* https://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.
*/
/**
* Repackaged launcher classes.
*
* @see org.springframework.boot.loader.launch.JarLauncher
* @see org.springframework.boot.loader.launch.WarLauncher
*/
package org.springframework.boot.loader.launch;

@ -57,7 +57,7 @@ class JarIntegrationTests extends AbstractArchiveIntegrationTests {
File repackaged = new File(project, "target/jar-0.0.1.BUILD-SNAPSHOT.jar"); File repackaged = new File(project, "target/jar-0.0.1.BUILD-SNAPSHOT.jar");
assertThat(launchScript(repackaged)).isEmpty(); assertThat(launchScript(repackaged)).isEmpty();
assertThat(jar(repackaged)).manifest((manifest) -> { assertThat(jar(repackaged)).manifest((manifest) -> {
manifest.hasMainClass("org.springframework.boot.loader.JarLauncher"); manifest.hasMainClass("org.springframework.boot.loader.launch.JarLauncher");
manifest.hasStartClass("some.random.Main"); manifest.hasStartClass("some.random.Main");
manifest.hasAttribute("Not-Used", "Foo"); manifest.hasAttribute("Not-Used", "Foo");
}) })
@ -66,7 +66,7 @@ class JarIntegrationTests extends AbstractArchiveIntegrationTests {
.hasEntryWithNameStartingWith("BOOT-INF/lib/spring-jcl") .hasEntryWithNameStartingWith("BOOT-INF/lib/spring-jcl")
.hasEntryWithNameStartingWith("BOOT-INF/lib/jakarta.servlet-api-6") .hasEntryWithNameStartingWith("BOOT-INF/lib/jakarta.servlet-api-6")
.hasEntryWithName("BOOT-INF/classes/org/test/SampleApplication.class") .hasEntryWithName("BOOT-INF/classes/org/test/SampleApplication.class")
.hasEntryWithName("org/springframework/boot/loader/JarLauncher.class"); .hasEntryWithName("org/springframework/boot/loader/launch/JarLauncher.class");
assertThat(buildLog(project)) assertThat(buildLog(project))
.contains("Replacing main artifact " + repackaged + " with repackaged archive,") .contains("Replacing main artifact " + repackaged + " with repackaged archive,")
.contains("The original artifact has been renamed to " + original) .contains("The original artifact has been renamed to " + original)
@ -273,8 +273,8 @@ class JarIntegrationTests extends AbstractArchiveIntegrationTests {
.goals("package", "-Dspring-boot.repackage.layout=ZIP") .goals("package", "-Dspring-boot.repackage.layout=ZIP")
.execute((project) -> { .execute((project) -> {
File main = new File(project, "target/jar-with-layout-property-0.0.1.BUILD-SNAPSHOT.jar"); File main = new File(project, "target/jar-with-layout-property-0.0.1.BUILD-SNAPSHOT.jar");
assertThat(jar(main)) assertThat(jar(main)).manifest(
.manifest((manifest) -> manifest.hasMainClass("org.springframework.boot.loader.PropertiesLauncher") (manifest) -> manifest.hasMainClass("org.springframework.boot.loader.launch.PropertiesLauncher")
.hasStartClass("org.test.SampleApplication")); .hasStartClass("org.test.SampleApplication"));
assertThat(buildLog(project)).contains("Layout: ZIP"); assertThat(buildLog(project)).contains("Layout: ZIP");
}); });
@ -284,8 +284,8 @@ class JarIntegrationTests extends AbstractArchiveIntegrationTests {
void whenALayoutIsConfiguredTheSpecifiedLayoutIsUsed(MavenBuild mavenBuild) { void whenALayoutIsConfiguredTheSpecifiedLayoutIsUsed(MavenBuild mavenBuild) {
mavenBuild.project("jar-with-zip-layout").execute((project) -> { mavenBuild.project("jar-with-zip-layout").execute((project) -> {
File main = new File(project, "target/jar-with-zip-layout-0.0.1.BUILD-SNAPSHOT.jar"); File main = new File(project, "target/jar-with-zip-layout-0.0.1.BUILD-SNAPSHOT.jar");
assertThat(jar(main)) assertThat(jar(main)).manifest(
.manifest((manifest) -> manifest.hasMainClass("org.springframework.boot.loader.PropertiesLauncher") (manifest) -> manifest.hasMainClass("org.springframework.boot.loader.launch.PropertiesLauncher")
.hasStartClass("org.test.SampleApplication")); .hasStartClass("org.test.SampleApplication"));
assertThat(buildLog(project)).contains("Layout: ZIP"); assertThat(buildLog(project)).contains("Layout: ZIP");
}); });

@ -57,10 +57,10 @@ class WarIntegrationTests extends AbstractArchiveIntegrationTests {
.hasEntryWithNameStartingWith("WEB-INF/lib/spring-core") .hasEntryWithNameStartingWith("WEB-INF/lib/spring-core")
.hasEntryWithNameStartingWith("WEB-INF/lib/spring-jcl") .hasEntryWithNameStartingWith("WEB-INF/lib/spring-jcl")
.hasEntryWithNameStartingWith("WEB-INF/lib-provided/jakarta.servlet-api-6") .hasEntryWithNameStartingWith("WEB-INF/lib-provided/jakarta.servlet-api-6")
.hasEntryWithName("org/springframework/boot/loader/WarLauncher.class") .hasEntryWithName("org/springframework/boot/loader/launch/WarLauncher.class")
.hasEntryWithName("WEB-INF/classes/org/test/SampleApplication.class") .hasEntryWithName("WEB-INF/classes/org/test/SampleApplication.class")
.hasEntryWithName("index.html") .hasEntryWithName("index.html")
.manifest((manifest) -> manifest.hasMainClass("org.springframework.boot.loader.WarLauncher") .manifest((manifest) -> manifest.hasMainClass("org.springframework.boot.loader.launch.WarLauncher")
.hasStartClass("org.test.SampleApplication") .hasStartClass("org.test.SampleApplication")
.hasAttribute("Not-Used", "Foo"))); .hasAttribute("Not-Used", "Foo")));
} }

@ -93,9 +93,10 @@ class PaketoBuilderTests {
.contains("paketo-buildpacks/ca-certificates", "paketo-buildpacks/bellsoft-liberica", .contains("paketo-buildpacks/ca-certificates", "paketo-buildpacks/bellsoft-liberica",
"paketo-buildpacks/executable-jar", "paketo-buildpacks/dist-zip", "paketo-buildpacks/executable-jar", "paketo-buildpacks/dist-zip",
"paketo-buildpacks/spring-boot"); "paketo-buildpacks/spring-boot");
metadata.processOfType("web").containsExactly("java", "org.springframework.boot.loader.JarLauncher"); metadata.processOfType("web")
.containsExactly("java", "org.springframework.boot.loader.launch.launch.JarLauncher");
metadata.processOfType("executable-jar") metadata.processOfType("executable-jar")
.containsExactly("java", "org.springframework.boot.loader.JarLauncher"); .containsExactly("java", "org.springframework.boot.loader.launch.launch.JarLauncher");
}); });
assertImageHasJvmSbomLayer(imageReference, config); assertImageHasJvmSbomLayer(imageReference, config);
assertImageHasDependenciesSbomLayer(imageReference, config, "executable-jar"); assertImageHasDependenciesSbomLayer(imageReference, config, "executable-jar");
@ -238,9 +239,10 @@ class PaketoBuilderTests {
.contains("paketo-buildpacks/ca-certificates", "paketo-buildpacks/bellsoft-liberica", .contains("paketo-buildpacks/ca-certificates", "paketo-buildpacks/bellsoft-liberica",
"paketo-buildpacks/executable-jar", "paketo-buildpacks/dist-zip", "paketo-buildpacks/executable-jar", "paketo-buildpacks/dist-zip",
"paketo-buildpacks/spring-boot"); "paketo-buildpacks/spring-boot");
metadata.processOfType("web").containsExactly("java", "org.springframework.boot.loader.WarLauncher"); metadata.processOfType("web")
.containsExactly("java", "org.springframework.boot.loader.launch.WarLauncher");
metadata.processOfType("executable-jar") metadata.processOfType("executable-jar")
.containsExactly("java", "org.springframework.boot.loader.WarLauncher"); .containsExactly("java", "org.springframework.boot.loader.launch.WarLauncher");
}); });
assertImageHasJvmSbomLayer(imageReference, config); assertImageHasJvmSbomLayer(imageReference, config);
assertImageHasDependenciesSbomLayer(imageReference, config, "executable-jar"); assertImageHasDependenciesSbomLayer(imageReference, config, "executable-jar");

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2021 the original author or authors. * Copyright 2012-2023 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -55,8 +55,8 @@ class ExplodedApplicationLauncher extends AbstractApplicationLauncher {
@Override @Override
protected List<String> getArguments(File archive, File serverPortFile) { protected List<String> getArguments(File archive, File serverPortFile) {
String mainClass = (archive.getName().endsWith(".war") ? "org.springframework.boot.loader.WarLauncher" String mainClass = (archive.getName().endsWith(".war") ? "org.springframework.boot.loader.launch.WarLauncher"
: "org.springframework.boot.loader.JarLauncher"); : "org.springframework.boot.loader.launch.JarLauncher");
try { try {
explodeArchive(archive); explodeArchive(archive);
return Arrays.asList("-cp", this.exploded.getAbsolutePath(), mainClass, serverPortFile.getAbsolutePath()); return Arrays.asList("-cp", this.exploded.getAbsolutePath(), mainClass, serverPortFile.getAbsolutePath());

@ -67,7 +67,7 @@
</mappedresources> </mappedresources>
<zipfileset src="${lib.dir}/loader/spring-boot-loader-jar-${ant-spring-boot.version}.jar" /> <zipfileset src="${lib.dir}/loader/spring-boot-loader-jar-${ant-spring-boot.version}.jar" />
<manifest> <manifest>
<attribute name="Main-Class" value="org.springframework.boot.loader.JarLauncher" /> <attribute name="Main-Class" value="org.springframework.boot.loader.launch.JarLauncher" />
<attribute name="Start-Class" value="${start-class}" /> <attribute name="Start-Class" value="${start-class}" />
</manifest> </manifest>
</jar> </jar>

Loading…
Cancel
Save