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 2acf9c6c6b..cf0aa0bf61 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 @@ -110,7 +110,8 @@ public class BootJar extends Jar implements BootArchive { private void moveMetaInfToRoot(CopySpec spec) { spec.eachFile((file) -> { String path = file.getRelativeSourcePath().getPathString(); - if (path.startsWith("META-INF/") && !path.equals("META-INF/aop.xml") && !path.endsWith(".kotlin_module")) { + if (path.startsWith("META-INF/") && !path.equals("META-INF/aop.xml") && !path.endsWith(".kotlin_module") + && !path.startsWith("META-INF/services/")) { this.support.moveToRoot(file); } }); diff --git a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/BootJarTests.java b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/BootJarTests.java index 45d3d341d9..833ed4c028 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/BootJarTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/BootJarTests.java @@ -163,6 +163,27 @@ class BootJarTests extends AbstractBootArchiveTests { } } + @Test + void metaInfServicesEntryIsPackagedBeneathClassesDirectory() throws IOException { + getTask().getMainClass().set("com.example.Main"); + File classpathDirectory = new File(this.temp, "classes"); + File service = new File(classpathDirectory, "META-INF/services/com.example.Service"); + service.getParentFile().mkdirs(); + service.createNewFile(); + File applicationClass = new File(classpathDirectory, "com/example/Application.class"); + applicationClass.getParentFile().mkdirs(); + applicationClass.createNewFile(); + getTask().classpath(classpathDirectory); + executeTask(); + try (JarFile jarFile = new JarFile(getTask().getArchiveFile().get().getAsFile())) { + assertThat(jarFile.getEntry("BOOT-INF/classes/com/example/Application.class")).isNotNull(); + assertThat(jarFile.getEntry("com/example/Application.class")).isNull(); + assertThat(jarFile.getEntry("BOOT-INF/classes/META-INF/services/com.example.Service")).isNotNull(); + assertThat(jarFile.getEntry("META-INF/services/com.example.Service")).isNull(); + } + + } + @Override void applyLayered(Action action) { getTask().layered(action); diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Packager.java b/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Packager.java index 8921aba23d..05baa33f84 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Packager.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/main/java/org/springframework/boot/loader/tools/Packager.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 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. @@ -452,7 +452,8 @@ public abstract class Packager { private boolean isTransformable(JarArchiveEntry entry) { String name = entry.getName(); if (name.startsWith("META-INF/")) { - return name.equals("META-INF/aop.xml") || name.endsWith(".kotlin_module"); + return name.equals("META-INF/aop.xml") || name.endsWith(".kotlin_module") + || name.startsWith("META-INF/services/"); } return !name.startsWith("BOOT-INF/") && !name.equals("module-info.class"); } diff --git a/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/test/java/org/springframework/boot/loader/tools/AbstractPackagerTests.java b/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/test/java/org/springframework/boot/loader/tools/AbstractPackagerTests.java index 83389a6e18..09c4e16757 100644 --- a/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/test/java/org/springframework/boot/loader/tools/AbstractPackagerTests.java +++ b/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/test/java/org/springframework/boot/loader/tools/AbstractPackagerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 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. @@ -484,6 +484,18 @@ abstract class AbstractPackagerTests

{ assertThat(getPackagedEntry("BOOT-INF/classes/META-INF/aop.xml")).isNotNull(); } + @Test + void metaInfServicesFilesAreMovedBeneathBootInfClassesWhenRepackaged() throws Exception { + this.testJarFile.addClass("A.class", ClassWithMainMethod.class); + File service = new File(this.tempDir, "com.example.Service"); + service.createNewFile(); + this.testJarFile.addFile("META-INF/services/com.example.Service", service); + P packager = createPackager(); + execute(packager, NO_LIBRARIES); + assertThat(getPackagedEntry("META-INF/services/com.example.Service")).isNull(); + assertThat(getPackagedEntry("BOOT-INF/classes/META-INF/services/com.example.Service")).isNotNull(); + } + @Test void allEntriesUseUnixPlatformAndUtf8NameEncoding() throws IOException { this.testJarFile.addClass("A.class", ClassWithMainMethod.class);