Add Maven and Gradle option for the loader implementation to use
Add properties to the Maven and Gradle plugins so that users can switch between the two loader modules. See gh-37669
This commit is contained in:
parent
a89057b7c7
commit
55b5610dd9
|
|
@ -33,6 +33,8 @@ import org.gradle.api.tasks.Input;
|
|||
import org.gradle.api.tasks.Nested;
|
||||
import org.gradle.api.tasks.Optional;
|
||||
|
||||
import org.springframework.boot.loader.tools.LoaderImplementation;
|
||||
|
||||
/**
|
||||
* A Spring Boot "fat" archive task.
|
||||
*
|
||||
|
|
@ -133,4 +135,13 @@ public interface BootArchive extends Task {
|
|||
*/
|
||||
void resolvedArtifacts(Provider<Set<ResolvedArtifactResult>> resolvedArtifacts);
|
||||
|
||||
/**
|
||||
* The loader implementation that should be used with the archive.
|
||||
* @return the loader implementation
|
||||
* @since 3.2.0
|
||||
*/
|
||||
@Input
|
||||
@Optional
|
||||
Property<LoaderImplementation> getLoaderImplementation();
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,6 +42,8 @@ import org.gradle.api.tasks.WorkResult;
|
|||
import org.gradle.api.tasks.bundling.Jar;
|
||||
import org.gradle.api.tasks.util.PatternSet;
|
||||
|
||||
import org.springframework.boot.loader.tools.LoaderImplementation;
|
||||
|
||||
/**
|
||||
* Support class for implementations of {@link BootArchive}.
|
||||
*
|
||||
|
|
@ -116,12 +118,13 @@ class BootArchiveSupport {
|
|||
return (version != null) ? version : "unknown";
|
||||
}
|
||||
|
||||
CopyAction createCopyAction(Jar jar, ResolvedDependencies resolvedDependencies) {
|
||||
return createCopyAction(jar, resolvedDependencies, null, null);
|
||||
CopyAction createCopyAction(Jar jar, ResolvedDependencies resolvedDependencies,
|
||||
LoaderImplementation loaderImplementation) {
|
||||
return createCopyAction(jar, resolvedDependencies, loaderImplementation, null, null);
|
||||
}
|
||||
|
||||
CopyAction createCopyAction(Jar jar, ResolvedDependencies resolvedDependencies, LayerResolver layerResolver,
|
||||
String layerToolsLocation) {
|
||||
CopyAction createCopyAction(Jar jar, ResolvedDependencies resolvedDependencies,
|
||||
LoaderImplementation loaderImplementation, LayerResolver layerResolver, String layerToolsLocation) {
|
||||
File output = jar.getArchiveFile().get().getAsFile();
|
||||
Manifest manifest = jar.getManifest();
|
||||
boolean preserveFileTimestamps = jar.isPreserveFileTimestamps();
|
||||
|
|
@ -136,7 +139,7 @@ class BootArchiveSupport {
|
|||
String encoding = jar.getMetadataCharset();
|
||||
CopyAction action = new BootZipCopyAction(output, manifest, preserveFileTimestamps, dirMode, fileMode,
|
||||
includeDefaultLoader, layerToolsLocation, requiresUnpack, exclusions, launchScript, librarySpec,
|
||||
compressionResolver, encoding, resolvedDependencies, layerResolver);
|
||||
compressionResolver, encoding, resolvedDependencies, layerResolver, loaderImplementation);
|
||||
return jar.isReproducibleFileOrder() ? new ReproducibleOrderingCopyAction(action) : action;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -37,6 +37,8 @@ import org.gradle.api.tasks.Nested;
|
|||
import org.gradle.api.tasks.bundling.Jar;
|
||||
import org.gradle.work.DisableCachingByDefault;
|
||||
|
||||
import org.springframework.boot.loader.tools.LoaderImplementation;
|
||||
|
||||
/**
|
||||
* A custom {@link Jar} task that produces a Spring Boot executable jar.
|
||||
*
|
||||
|
|
@ -141,12 +143,14 @@ public abstract class BootJar extends Jar implements BootArchive {
|
|||
|
||||
@Override
|
||||
protected CopyAction createCopyAction() {
|
||||
LoaderImplementation loaderImplementation = getLoaderImplementation().getOrElse(LoaderImplementation.DEFAULT);
|
||||
if (!isLayeredDisabled()) {
|
||||
LayerResolver layerResolver = new LayerResolver(this.resolvedDependencies, this.layered, this::isLibrary);
|
||||
String layerToolsLocation = this.layered.getIncludeLayerTools().get() ? LIB_DIRECTORY : null;
|
||||
return this.support.createCopyAction(this, this.resolvedDependencies, layerResolver, layerToolsLocation);
|
||||
return this.support.createCopyAction(this, this.resolvedDependencies, loaderImplementation, layerResolver,
|
||||
layerToolsLocation);
|
||||
}
|
||||
return this.support.createCopyAction(this, this.resolvedDependencies);
|
||||
return this.support.createCopyAction(this, this.resolvedDependencies, loaderImplementation);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -37,6 +37,8 @@ import org.gradle.api.tasks.Optional;
|
|||
import org.gradle.api.tasks.bundling.War;
|
||||
import org.gradle.work.DisableCachingByDefault;
|
||||
|
||||
import org.springframework.boot.loader.tools.LoaderImplementation;
|
||||
|
||||
/**
|
||||
* A custom {@link War} task that produces a Spring Boot executable war.
|
||||
*
|
||||
|
|
@ -115,12 +117,14 @@ public abstract class BootWar extends War implements BootArchive {
|
|||
|
||||
@Override
|
||||
protected CopyAction createCopyAction() {
|
||||
LoaderImplementation loaderImplementation = getLoaderImplementation().getOrElse(LoaderImplementation.DEFAULT);
|
||||
if (!isLayeredDisabled()) {
|
||||
LayerResolver layerResolver = new LayerResolver(this.resolvedDependencies, this.layered, this::isLibrary);
|
||||
String layerToolsLocation = this.layered.getIncludeLayerTools().get() ? LIB_DIRECTORY : null;
|
||||
return this.support.createCopyAction(this, this.resolvedDependencies, layerResolver, layerToolsLocation);
|
||||
return this.support.createCopyAction(this, this.resolvedDependencies, loaderImplementation, layerResolver,
|
||||
layerToolsLocation);
|
||||
}
|
||||
return this.support.createCopyAction(this, this.resolvedDependencies);
|
||||
return this.support.createCopyAction(this, this.resolvedDependencies, loaderImplementation);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ import org.springframework.boot.loader.tools.JarModeLibrary;
|
|||
import org.springframework.boot.loader.tools.Layer;
|
||||
import org.springframework.boot.loader.tools.LayersIndex;
|
||||
import org.springframework.boot.loader.tools.LibraryCoordinates;
|
||||
import org.springframework.boot.loader.tools.LoaderImplementation;
|
||||
import org.springframework.boot.loader.tools.NativeImageArgFile;
|
||||
import org.springframework.boot.loader.tools.ReachabilityMetadataProperties;
|
||||
import org.springframework.util.Assert;
|
||||
|
|
@ -111,11 +112,14 @@ class BootZipCopyAction implements CopyAction {
|
|||
|
||||
private final LayerResolver layerResolver;
|
||||
|
||||
private final LoaderImplementation loaderImplementation;
|
||||
|
||||
BootZipCopyAction(File output, Manifest manifest, boolean preserveFileTimestamps, Integer dirMode, Integer fileMode,
|
||||
boolean includeDefaultLoader, String layerToolsLocation, Spec<FileTreeElement> requiresUnpack,
|
||||
Spec<FileTreeElement> exclusions, LaunchScriptConfiguration launchScript, Spec<FileCopyDetails> librarySpec,
|
||||
Function<FileCopyDetails, ZipCompression> compressionResolver, String encoding,
|
||||
ResolvedDependencies resolvedDependencies, LayerResolver layerResolver) {
|
||||
ResolvedDependencies resolvedDependencies, LayerResolver layerResolver,
|
||||
LoaderImplementation loaderImplementation) {
|
||||
this.output = output;
|
||||
this.manifest = manifest;
|
||||
this.preserveFileTimestamps = preserveFileTimestamps;
|
||||
|
|
@ -131,6 +135,7 @@ class BootZipCopyAction implements CopyAction {
|
|||
this.encoding = encoding;
|
||||
this.resolvedDependencies = resolvedDependencies;
|
||||
this.layerResolver = layerResolver;
|
||||
this.loaderImplementation = loaderImplementation;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -310,7 +315,8 @@ class BootZipCopyAction implements CopyAction {
|
|||
// Always write loader entries after META-INF directory (see gh-16698)
|
||||
return;
|
||||
}
|
||||
LoaderZipEntries loaderEntries = new LoaderZipEntries(getTime(), getDirMode(), getFileMode());
|
||||
LoaderZipEntries loaderEntries = new LoaderZipEntries(getTime(), getDirMode(), getFileMode(),
|
||||
BootZipCopyAction.this.loaderImplementation);
|
||||
this.writtenLoaderEntries = loaderEntries.writeTo(this.out);
|
||||
if (BootZipCopyAction.this.layerResolver != null) {
|
||||
for (String name : this.writtenLoaderEntries.getFiles()) {
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
|
|||
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
|
||||
import org.gradle.api.file.FileTreeElement;
|
||||
|
||||
import org.springframework.boot.loader.tools.LoaderImplementation;
|
||||
import org.springframework.util.StreamUtils;
|
||||
|
||||
/**
|
||||
|
|
@ -39,22 +40,26 @@ import org.springframework.util.StreamUtils;
|
|||
*/
|
||||
class LoaderZipEntries {
|
||||
|
||||
private final LoaderImplementation loaderImplementation;
|
||||
|
||||
private final Long entryTime;
|
||||
|
||||
private final int dirMode;
|
||||
|
||||
private final int fileMode;
|
||||
|
||||
LoaderZipEntries(Long entryTime, int dirMode, int fileMode) {
|
||||
LoaderZipEntries(Long entryTime, int dirMode, int fileMode, LoaderImplementation loaderImplementation) {
|
||||
this.entryTime = entryTime;
|
||||
this.dirMode = dirMode;
|
||||
this.fileMode = fileMode;
|
||||
this.loaderImplementation = (loaderImplementation != null) ? loaderImplementation
|
||||
: LoaderImplementation.DEFAULT;
|
||||
}
|
||||
|
||||
WrittenEntries writeTo(ZipArchiveOutputStream out) throws IOException {
|
||||
WrittenEntries written = new WrittenEntries();
|
||||
try (ZipInputStream loaderJar = new ZipInputStream(
|
||||
getClass().getResourceAsStream("/META-INF/loader/spring-boot-loader-classic.jar"))) {
|
||||
getClass().getResourceAsStream("/" + this.loaderImplementation.getJarResourceName()))) {
|
||||
java.util.zip.ZipEntry entry = loaderJar.getNextEntry();
|
||||
while (entry != null) {
|
||||
if (entry.isDirectory() && !entry.getName().equals("META-INF/")) {
|
||||
|
|
|
|||
|
|
@ -106,6 +106,16 @@ abstract class AbstractBootArchiveIntegrationTests {
|
|||
assertThat(firstHash).isEqualTo(secondHash);
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
void classicLoader() throws IOException {
|
||||
assertThat(this.gradleBuild.build(this.taskName).task(":" + this.taskName).getOutcome())
|
||||
.isEqualTo(TaskOutcome.SUCCESS);
|
||||
File jar = new File(this.gradleBuild.getProjectDir(), "build/libs").listFiles()[0];
|
||||
try (JarFile jarFile = new JarFile(jar)) {
|
||||
assertThat(jarFile.getEntry("org/springframework/boot/loader/LaunchedURLClassLoader.class")).isNotNull();
|
||||
}
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
void upToDateWhenBuiltTwice() {
|
||||
assertThat(this.gradleBuild.build(this.taskName).task(":" + this.taskName).getOutcome())
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ import org.junit.jupiter.api.io.TempDir;
|
|||
import org.springframework.boot.gradle.junit.GradleProjectBuilder;
|
||||
import org.springframework.boot.loader.tools.DefaultLaunchScript;
|
||||
import org.springframework.boot.loader.tools.JarModeLibrary;
|
||||
import org.springframework.boot.loader.tools.LoaderImplementation;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
|
@ -279,6 +280,17 @@ abstract class AbstractBootArchiveTests<T extends Jar & BootArchive> {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void loaderIsWrittenToTheRootOfTheJarWhenUsingClassicLoader() throws IOException {
|
||||
this.task.getMainClass().set("com.example.Main");
|
||||
this.task.getLoaderImplementation().set(LoaderImplementation.CLASSIC);
|
||||
executeTask();
|
||||
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/")).isNotNull();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void unpackCommentIsAddedToEntryIdentifiedByAPattern() throws IOException {
|
||||
this.task.getMainClass().set("com.example.Main");
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
plugins {
|
||||
id 'java'
|
||||
id 'org.springframework.boot' version '{version}'
|
||||
}
|
||||
|
||||
bootJar {
|
||||
mainClass = 'com.example.Application'
|
||||
loaderImplementation = org.springframework.boot.loader.tools.LoaderImplementation.CLASSIC
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
plugins {
|
||||
id 'war'
|
||||
id 'org.springframework.boot' version '{version}'
|
||||
}
|
||||
|
||||
bootWar {
|
||||
mainClass = 'com.example.Application'
|
||||
loaderImplementation = org.springframework.boot.loader.tools.LoaderImplementation.CLASSIC
|
||||
}
|
||||
|
|
@ -13,6 +13,10 @@ configurations {
|
|||
extendsFrom dependencyManagement
|
||||
transitive = false
|
||||
}
|
||||
loaderClassic {
|
||||
extendsFrom dependencyManagement
|
||||
transitive = false
|
||||
}
|
||||
jarmode {
|
||||
extendsFrom dependencyManagement
|
||||
transitive = false
|
||||
|
|
@ -36,7 +40,8 @@ dependencies {
|
|||
|
||||
compileOnly("ch.qos.logback:logback-classic")
|
||||
|
||||
loader(project(":spring-boot-project:spring-boot-tools:spring-boot-loader-classic"))
|
||||
loader(project(":spring-boot-project:spring-boot-tools:spring-boot-loader"))
|
||||
loaderClassic(project(":spring-boot-project:spring-boot-tools:spring-boot-loader-classic"))
|
||||
|
||||
jarmode(project(":spring-boot-project:spring-boot-tools:spring-boot-jarmode-layertools"))
|
||||
|
||||
|
|
@ -57,6 +62,21 @@ task reproducibleLoaderJar(type: Jar) {
|
|||
}
|
||||
reproducibleFileOrder = true
|
||||
preserveFileTimestamps = false
|
||||
archiveFileName = "spring-boot-loader.jar"
|
||||
destinationDirectory = file("${generatedResources}/META-INF/loader")
|
||||
}
|
||||
|
||||
task reproducibleLoaderClassicJar(type: Jar) {
|
||||
dependsOn configurations.loaderClassic
|
||||
from {
|
||||
zipTree(configurations.loaderClassic.incoming.files.singleFile).matching {
|
||||
exclude "META-INF/LICENSE.txt"
|
||||
exclude "META-INF/NOTICE.txt"
|
||||
exclude "META-INF/spring-boot.properties"
|
||||
}
|
||||
}
|
||||
reproducibleFileOrder = true
|
||||
preserveFileTimestamps = false
|
||||
archiveFileName = "spring-boot-loader-classic.jar"
|
||||
destinationDirectory = file("${generatedResources}/META-INF/loader")
|
||||
}
|
||||
|
|
@ -72,7 +92,7 @@ task layerToolsJar(type: Sync) {
|
|||
|
||||
sourceSets {
|
||||
main {
|
||||
output.dir(generatedResources, builtBy: [layerToolsJar, reproducibleLoaderJar])
|
||||
output.dir(generatedResources, builtBy: [layerToolsJar, reproducibleLoaderJar, reproducibleLoaderClassicJar])
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -51,8 +51,6 @@ import org.apache.commons.compress.archivers.zip.UnixStat;
|
|||
*/
|
||||
public abstract class AbstractJarWriter implements LoaderClassesWriter {
|
||||
|
||||
private static final String NESTED_LOADER_JAR = "META-INF/loader/spring-boot-loader-classic.jar";
|
||||
|
||||
private static final int BUFFER_SIZE = 32 * 1024;
|
||||
|
||||
private static final int UNIX_FILE_MODE = UnixStat.FILE_FLAG | UnixStat.DEFAULT_FILE_PERM;
|
||||
|
|
@ -199,13 +197,15 @@ public abstract class AbstractJarWriter implements LoaderClassesWriter {
|
|||
return library.getLastModified();
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the required spring-boot-loader classes to the JAR.
|
||||
* @throws IOException if the classes cannot be written
|
||||
*/
|
||||
@Override
|
||||
public void writeLoaderClasses() throws IOException {
|
||||
writeLoaderClasses(NESTED_LOADER_JAR);
|
||||
writeLoaderClasses(LoaderImplementation.DEFAULT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeLoaderClasses(LoaderImplementation loaderImplementation) throws IOException {
|
||||
writeLoaderClasses((loaderImplementation != null) ? loaderImplementation.getJarResourceName()
|
||||
: LoaderImplementation.DEFAULT.getJarResourceName());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ import java.util.Locale;
|
|||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Common {@link Layout}s.
|
||||
* Common {@link Layout layouts}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Dave Syer
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2019 the original author or authors.
|
||||
* 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.
|
||||
|
|
@ -34,6 +34,14 @@ public interface LoaderClassesWriter {
|
|||
*/
|
||||
void writeLoaderClasses() throws IOException;
|
||||
|
||||
/**
|
||||
* Write the default required spring-boot-loader classes to the JAR.
|
||||
* @param loaderImplementation the specific implementation to write
|
||||
* @throws IOException if the classes cannot be written
|
||||
* @since 3.2.0
|
||||
*/
|
||||
void writeLoaderClasses(LoaderImplementation loaderImplementation) throws IOException;
|
||||
|
||||
/**
|
||||
* Write custom required spring-boot-loader classes to the JAR.
|
||||
* @param loaderJarResourceName the name of the resource containing the loader classes
|
||||
|
|
|
|||
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* 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.tools;
|
||||
|
||||
/**
|
||||
* Supported loader implementations.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 3.2.0
|
||||
*/
|
||||
public enum LoaderImplementation {
|
||||
|
||||
/**
|
||||
* The default recommended loader implementation.
|
||||
*/
|
||||
DEFAULT("META-INF/loader/spring-boot-loader.jar"),
|
||||
|
||||
/**
|
||||
* The classic loader implementation as used with Spring Boot 3.1 and earlier.
|
||||
*/
|
||||
CLASSIC("META-INF/loader/spring-boot-loader-classic.jar");
|
||||
|
||||
private final String jarResourceName;
|
||||
|
||||
LoaderImplementation(String jarResourceName) {
|
||||
this.jarResourceName = jarResourceName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the nested resource that can be loaded from the tools jar.
|
||||
* @return the jar resource name
|
||||
*/
|
||||
public String getJarResourceName() {
|
||||
return this.jarResourceName;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -88,6 +88,8 @@ public abstract class Packager {
|
|||
|
||||
private Layout layout;
|
||||
|
||||
private LoaderImplementation loaderImplementation;
|
||||
|
||||
private LayoutFactory layoutFactory;
|
||||
|
||||
private Layers layers;
|
||||
|
|
@ -135,6 +137,14 @@ public abstract class Packager {
|
|||
this.layout = layout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the loader implementation to use.
|
||||
* @param loaderImplementation the loaderImplementation to set
|
||||
*/
|
||||
public void setLoaderImplementation(LoaderImplementation loaderImplementation) {
|
||||
this.loaderImplementation = loaderImplementation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the layout factory for the jar. The factory can be used when no specific
|
||||
* layout is specified.
|
||||
|
|
@ -215,7 +225,7 @@ public abstract class Packager {
|
|||
customLoaderLayout.writeLoadedClasses(writer);
|
||||
}
|
||||
else if (layout.isExecutable()) {
|
||||
writer.writeLoaderClasses();
|
||||
writer.writeLoaderClasses(this.loaderImplementation);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -75,6 +75,26 @@ class JarIntegrationTests extends AbstractArchiveIntegrationTests {
|
|||
});
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
void whenJarWithClassicLoaderIsRepackagedInPlaceOnlyRepackagedJarIsInstalled(MavenBuild mavenBuild) {
|
||||
mavenBuild.project("jar-with-classic-loader").goals("install").execute((project) -> {
|
||||
File original = new File(project, "target/jar-with-classic-loader-0.0.1.BUILD-SNAPSHOT.jar.original");
|
||||
assertThat(original).isFile();
|
||||
File repackaged = new File(project, "target/jar-with-classic-loader-0.0.1.BUILD-SNAPSHOT.jar");
|
||||
assertThat(launchScript(repackaged)).isEmpty();
|
||||
assertThat(jar(repackaged)).manifest((manifest) -> {
|
||||
manifest.hasMainClass("org.springframework.boot.loader.launch.JarLauncher");
|
||||
manifest.hasStartClass("some.random.Main");
|
||||
manifest.hasAttribute("Not-Used", "Foo");
|
||||
}).hasEntryWithName("org/springframework/boot/loader/launch/JarLauncher.class");
|
||||
assertThat(buildLog(project))
|
||||
.contains("Replacing main artifact " + repackaged + " with repackaged archive,")
|
||||
.contains("The original artifact has been renamed to " + original)
|
||||
.contains("Installing " + repackaged + " to")
|
||||
.doesNotContain("Installing " + original + " to");
|
||||
});
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
void whenAttachIsDisabledOnlyTheOriginalJarIsInstalled(MavenBuild mavenBuild) {
|
||||
mavenBuild.project("jar-attach-disabled").goals("install").execute((project) -> {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,58 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.springframework.boot.maven.it</groupId>
|
||||
<artifactId>jar-with-classic-loader</artifactId>
|
||||
<version>0.0.1.BUILD-SNAPSHOT</version>
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.source>@java.version@</maven.compiler.source>
|
||||
<maven.compiler.target>@java.version@</maven.compiler.target>
|
||||
</properties>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>@project.groupId@</groupId>
|
||||
<artifactId>@project.artifactId@</artifactId>
|
||||
<version>@project.version@</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>@maven-jar-plugin.version@</version>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifest>
|
||||
<mainClass>some.random.Main</mainClass>
|
||||
</manifest>
|
||||
<manifestEntries>
|
||||
<Not-Used>Foo</Not-Used>
|
||||
</manifestEntries>
|
||||
</archive>
|
||||
<loaderImplementation>CLASSIC</loaderImplementation>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context</artifactId>
|
||||
<version>@spring-framework.version@</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>jakarta.servlet</groupId>
|
||||
<artifactId>jakarta.servlet-api</artifactId>
|
||||
<version>@jakarta-servlet.version@</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* 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.test;
|
||||
|
||||
public class SampleApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -47,6 +47,7 @@ import org.springframework.boot.loader.tools.Layouts.Jar;
|
|||
import org.springframework.boot.loader.tools.Layouts.None;
|
||||
import org.springframework.boot.loader.tools.Layouts.War;
|
||||
import org.springframework.boot.loader.tools.Libraries;
|
||||
import org.springframework.boot.loader.tools.LoaderImplementation;
|
||||
import org.springframework.boot.loader.tools.Packager;
|
||||
import org.springframework.boot.loader.tools.layer.CustomLayers;
|
||||
|
||||
|
|
@ -128,6 +129,15 @@ public abstract class AbstractPackagerMojo extends AbstractDependencyFilterMojo
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the loader implementation that should be used.
|
||||
* @return the loader implementation or {@code null}
|
||||
* @since 3.2.0
|
||||
*/
|
||||
protected LoaderImplementation getLoaderImplementation() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the layout factory that will be used to determine the {@link LayoutType} if
|
||||
* no explicit layout is set.
|
||||
|
|
@ -145,6 +155,7 @@ public abstract class AbstractPackagerMojo extends AbstractDependencyFilterMojo
|
|||
*/
|
||||
protected <P extends Packager> P getConfiguredPackager(Supplier<P> supplier) {
|
||||
P packager = supplier.get();
|
||||
packager.setLoaderImplementation(getLoaderImplementation());
|
||||
packager.setLayoutFactory(getLayoutFactory());
|
||||
packager.addMainClassTimeoutWarningListener(new LoggingMainClassTimeoutWarningListener(this::getLog));
|
||||
packager.setMainClass(this.mainClass);
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ import org.springframework.boot.loader.tools.EntryWriter;
|
|||
import org.springframework.boot.loader.tools.ImagePackager;
|
||||
import org.springframework.boot.loader.tools.LayoutFactory;
|
||||
import org.springframework.boot.loader.tools.Libraries;
|
||||
import org.springframework.boot.loader.tools.LoaderImplementation;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
|
|
@ -187,6 +188,13 @@ public abstract class BuildImageMojo extends AbstractPackagerMojo {
|
|||
@Parameter
|
||||
private LayoutType layout;
|
||||
|
||||
/**
|
||||
* The loader implementation that should be used.
|
||||
* @since 3.2.0
|
||||
*/
|
||||
@Parameter
|
||||
private LoaderImplementation loaderImplementation;
|
||||
|
||||
/**
|
||||
* The layout factory that will be used to create the executable archive if no
|
||||
* explicit layout is set. Alternative layouts implementations can be provided by 3rd
|
||||
|
|
@ -206,6 +214,11 @@ public abstract class BuildImageMojo extends AbstractPackagerMojo {
|
|||
return this.layout;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LoaderImplementation getLoaderImplementation() {
|
||||
return this.loaderImplementation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the layout factory that will be used to determine the
|
||||
* {@link AbstractPackagerMojo.LayoutType} if no explicit layout is set.
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ import org.springframework.boot.loader.tools.DefaultLaunchScript;
|
|||
import org.springframework.boot.loader.tools.LaunchScript;
|
||||
import org.springframework.boot.loader.tools.LayoutFactory;
|
||||
import org.springframework.boot.loader.tools.Libraries;
|
||||
import org.springframework.boot.loader.tools.LoaderImplementation;
|
||||
import org.springframework.boot.loader.tools.Repackager;
|
||||
|
||||
/**
|
||||
|
|
@ -161,6 +162,13 @@ public class RepackageMojo extends AbstractPackagerMojo {
|
|||
@Parameter(property = "spring-boot.repackage.layout")
|
||||
private LayoutType layout;
|
||||
|
||||
/**
|
||||
* The loader implementation that should be used.
|
||||
* @since 3.2.0
|
||||
*/
|
||||
@Parameter
|
||||
private LoaderImplementation loaderImplementation;
|
||||
|
||||
/**
|
||||
* The layout factory that will be used to create the executable archive if no
|
||||
* explicit layout is set. Alternative layouts implementations can be provided by 3rd
|
||||
|
|
@ -180,6 +188,11 @@ public class RepackageMojo extends AbstractPackagerMojo {
|
|||
return this.layout;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LoaderImplementation getLoaderImplementation() {
|
||||
return this.loaderImplementation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the layout factory that will be used to determine the
|
||||
* {@link AbstractPackagerMojo.LayoutType} if no explicit layout is set.
|
||||
|
|
|
|||
|
|
@ -16,3 +16,7 @@ dependencies {
|
|||
implementation("org.springframework.boot:spring-boot-starter-web")
|
||||
implementation("org.webjars:jquery:3.5.0")
|
||||
}
|
||||
|
||||
bootJar {
|
||||
loaderImplementation = org.springframework.boot.loader.tools.LoaderImplementation.CLASSIC
|
||||
}
|
||||
Loading…
Reference in New Issue