diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootArchiveSupport.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootArchiveSupport.java index 7335090fa5bd..800e826a89d2 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootArchiveSupport.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootArchiveSupport.java @@ -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"); * you may not use this file except in compliance with the License. @@ -54,6 +54,8 @@ class BootArchiveSupport { private static final byte[] ZIP_FILE_HEADER = new byte[] { 'P', 'K', 3, 4 }; + private static final String UNSPECIFIED_VERSION = "unspecified"; + private static final Set DEFAULT_LAUNCHER_CLASSES; static { @@ -85,7 +87,7 @@ class BootArchiveSupport { } void configureManifest(Manifest manifest, String mainClass, String classes, String lib, String classPathIndex, - String layersIndex, String jdkVersion) { + String layersIndex, String jdkVersion, String implementationName, Object implementationVersion) { Attributes attributes = manifest.getAttributes(); attributes.putIfAbsent("Main-Class", this.loaderMainClass); attributes.putIfAbsent("Start-Class", mainClass); @@ -99,6 +101,13 @@ void configureManifest(Manifest manifest, String mainClass, String classes, Stri attributes.putIfAbsent("Spring-Boot-Layers-Index", layersIndex); } attributes.putIfAbsent("Build-Jdk-Spec", jdkVersion); + attributes.putIfAbsent("Implementation-Name", implementationName); + if (implementationVersion != null) { + String versionString = implementationVersion.toString(); + if (!UNSPECIFIED_VERSION.equals(versionString)) { + attributes.putIfAbsent("Implementation-Version", versionString); + } + } } private String determineSpringBootVersion() { diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootJar.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootJar.java index cbbb07ec305d..b9dfd52a4d6d 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootJar.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootJar.java @@ -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"); * you may not use this file except in compliance with the License. @@ -29,6 +29,7 @@ import org.gradle.api.file.FileCopyDetails; import org.gradle.api.file.FileTreeElement; import org.gradle.api.internal.file.copy.CopyAction; +import org.gradle.api.provider.Provider; import org.gradle.api.specs.Spec; import org.gradle.api.tasks.Internal; import org.gradle.api.tasks.Nested; @@ -65,6 +66,10 @@ public abstract class BootJar extends Jar implements BootArchive { private final LayeredSpec layered; + private final Provider projectName; + + private final Provider projectVersion; + private FileCollection classpath; /** @@ -85,6 +90,8 @@ public BootJar() { } }); }); + this.projectName = project.provider(project::getName); + this.projectVersion = project.provider(project::getVersion); } private void configureBootInfSpec(CopySpec bootInfSpec) { @@ -120,7 +127,7 @@ private void moveMetaInfToRoot(CopySpec spec) { public void copy() { this.support.configureManifest(getManifest(), getMainClass().get(), CLASSES_DIRECTORY, LIB_DIRECTORY, CLASSPATH_INDEX, (isLayeredDisabled()) ? null : LAYERS_INDEX, - this.getTargetJavaVersion().get().getMajorVersion()); + this.getTargetJavaVersion().get().getMajorVersion(), this.projectName.get(), this.projectVersion.get()); super.copy(); } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootWar.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootWar.java index 0ad92c183b1c..76b1f756012c 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootWar.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/BootWar.java @@ -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"); * you may not use this file except in compliance with the License. @@ -28,6 +28,7 @@ import org.gradle.api.file.FileCopyDetails; import org.gradle.api.file.FileTreeElement; import org.gradle.api.internal.file.copy.CopyAction; +import org.gradle.api.provider.Provider; import org.gradle.api.specs.Spec; import org.gradle.api.tasks.Classpath; import org.gradle.api.tasks.Internal; @@ -65,6 +66,10 @@ public abstract class BootWar extends War implements BootArchive { private final LayeredSpec layered; + private final Provider projectName; + + private final Provider projectVersion; + private FileCollection providedClasspath; /** @@ -85,6 +90,8 @@ public BootWar() { } }); }); + this.projectName = project.provider(project::getName); + this.projectVersion = project.provider(project::getVersion); } private Object getProvidedLibFiles() { @@ -95,7 +102,7 @@ private Object getProvidedLibFiles() { public void copy() { this.support.configureManifest(getManifest(), getMainClass().get(), CLASSES_DIRECTORY, LIB_DIRECTORY, CLASSPATH_INDEX, (isLayeredDisabled()) ? null : LAYERS_INDEX, - this.getTargetJavaVersion().get().getMajorVersion()); + this.getTargetJavaVersion().get().getMajorVersion(), this.projectName.get(), this.projectVersion.get()); super.copy(); } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/AbstractBootArchiveTests.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/AbstractBootArchiveTests.java index 4caa3fc3e3e6..96be257ba063 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/AbstractBootArchiveTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/AbstractBootArchiveTests.java @@ -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"); * you may not use this file except in compliance with the License. @@ -132,6 +132,42 @@ void basicArchiveCreation() throws IOException { .isEqualTo(this.classesPath); assertThat(jarFile.getManifest().getMainAttributes().getValue("Spring-Boot-Lib")).isEqualTo(this.libPath); assertThat(jarFile.getManifest().getMainAttributes().getValue("Spring-Boot-Version")).isNotNull(); + assertThat(jarFile.getManifest().getMainAttributes().getValue("Implementation-Name")) + .isEqualTo(this.project.getName()); + assertThat(jarFile.getManifest().getMainAttributes().getValue("Implementation-Version")).isNull(); + } + } + + @Test + void whenImplementationNameIsCustomizedItShouldAppearInArchiveManifest() throws IOException { + this.task.getMainClass().set("com.example.Main"); + this.task.getManifest().getAttributes().put("Implementation-Name", "Customized"); + executeTask(); + try (JarFile jarFile = new JarFile(this.task.getArchiveFile().get().getAsFile())) { + assertThat(jarFile.getManifest().getMainAttributes().getValue("Implementation-Name")) + .isEqualTo("Customized"); + } + } + + @Test + void whenProjectVersionIsSetThenImplementationVersionShouldAppearInArchiveManifest() throws IOException { + this.project.setVersion("1.0.0"); + this.task.getMainClass().set("com.example.Main"); + executeTask(); + try (JarFile jarFile = new JarFile(this.task.getArchiveFile().get().getAsFile())) { + assertThat(jarFile.getManifest().getMainAttributes().getValue("Implementation-Version")).isEqualTo("1.0.0"); + } + } + + @Test + void whenImplementationVersionIsCustomizedItShouldAppearInArchiveManifest() throws IOException { + this.project.setVersion("1.0.0"); + this.task.getMainClass().set("com.example.Main"); + this.task.getManifest().getAttributes().put("Implementation-Version", "Customized"); + executeTask(); + try (JarFile jarFile = new JarFile(this.task.getArchiveFile().get().getAsFile())) { + assertThat(jarFile.getManifest().getMainAttributes().getValue("Implementation-Version")) + .isEqualTo("Customized"); } }