Skip to content

Commit 7671561

Browse files
committed
Merge pull request #21207 from PaddyDrury
* gh-21207: Polish "Stop limiting layer customization to external modules" Stop limiting layer customization to external modules Closes gh-21207
2 parents fe347df + 2d769e7 commit 7671561

File tree

4 files changed

+205
-44
lines changed

4 files changed

+205
-44
lines changed

spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/LayerResolver.java

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,10 @@
2424
import java.util.Map;
2525
import java.util.Set;
2626

27-
import org.gradle.api.artifacts.ArtifactCollection;
2827
import org.gradle.api.artifacts.Configuration;
29-
import org.gradle.api.artifacts.component.ComponentIdentifier;
30-
import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
31-
import org.gradle.api.artifacts.result.ResolvedArtifactResult;
28+
import org.gradle.api.artifacts.ModuleVersionIdentifier;
29+
import org.gradle.api.artifacts.ResolvedArtifact;
30+
import org.gradle.api.artifacts.ResolvedConfiguration;
3231
import org.gradle.api.file.FileCopyDetails;
3332
import org.gradle.api.specs.Spec;
3433

@@ -43,6 +42,7 @@
4342
* @author Madhura Bhave
4443
* @author Scott Frederick
4544
* @author Phillip Webb
45+
* @author Paddy Drury
4646
* @see BootZipCopyAction
4747
*/
4848
class LayerResolver {
@@ -110,7 +110,7 @@ private void processConfiguration(Configuration configuration) {
110110
if (configuration.isCanBeResolved()
111111
&& !DEPRECATED_FOR_RESOLUTION_CONFIGURATIONS.contains(configuration.getName())) {
112112
this.configurationDependencies.put(configuration,
113-
new ResolvedConfigurationDependencies(configuration.getIncoming().getArtifacts()));
113+
new ResolvedConfigurationDependencies(configuration.getResolvedConfiguration()));
114114
}
115115
}
116116

@@ -133,16 +133,10 @@ private static class ResolvedConfigurationDependencies {
133133

134134
private final Map<File, LibraryCoordinates> artifactCoordinates = new LinkedHashMap<>();
135135

136-
ResolvedConfigurationDependencies(ArtifactCollection resolvedDependencies) {
137-
if (resolvedDependencies != null) {
138-
for (ResolvedArtifactResult resolvedArtifact : resolvedDependencies.getArtifacts()) {
139-
ComponentIdentifier identifier = resolvedArtifact.getId().getComponentIdentifier();
140-
if (identifier instanceof ModuleComponentIdentifier) {
141-
this.artifactCoordinates.put(resolvedArtifact.getFile(),
142-
new ModuleComponentIdentifierLibraryCoordinates(
143-
(ModuleComponentIdentifier) identifier));
144-
}
145-
}
136+
ResolvedConfigurationDependencies(ResolvedConfiguration resolvedConfiguration) {
137+
for (ResolvedArtifact resolvedArtifact : resolvedConfiguration.getResolvedArtifacts()) {
138+
this.artifactCoordinates.put(resolvedArtifact.getFile(),
139+
new ModuleVersionIdentifierLibraryCoordinates(resolvedArtifact.getModuleVersion().getId()));
146140
}
147141
}
148142

@@ -153,13 +147,13 @@ LibraryCoordinates find(File file) {
153147
}
154148

155149
/**
156-
* Adapts a {@link ModuleComponentIdentifier} to {@link LibraryCoordinates}.
150+
* Adapts a {@link ModuleVersionIdentifier} to {@link LibraryCoordinates}.
157151
*/
158-
private static class ModuleComponentIdentifierLibraryCoordinates implements LibraryCoordinates {
152+
private static class ModuleVersionIdentifierLibraryCoordinates implements LibraryCoordinates {
159153

160-
private final ModuleComponentIdentifier identifier;
154+
private final ModuleVersionIdentifier identifier;
161155

162-
ModuleComponentIdentifierLibraryCoordinates(ModuleComponentIdentifier identifier) {
156+
ModuleVersionIdentifierLibraryCoordinates(ModuleVersionIdentifier identifier) {
163157
this.identifier = identifier;
164158
}
165159

@@ -170,7 +164,7 @@ public String getGroupId() {
170164

171165
@Override
172166
public String getArtifactId() {
173-
return this.identifier.getModule();
167+
return this.identifier.getName();
174168
}
175169

176170
@Override

spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/BootJarIntegrationTests.java

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
*
5454
* @author Andy Wilkinson
5555
* @author Madhura Bhave
56+
* @author Paddy Drury
5657
*/
5758
class BootJarIntegrationTests extends AbstractBootArchiveIntegrationTests {
5859

@@ -176,6 +177,64 @@ void customLayers() throws IOException {
176177
assertExtractedLayers(layerNames, indexedLayers);
177178
}
178179

180+
@TestTemplate
181+
void multiModuleCustomLayers() throws IOException {
182+
writeSettingsGradle();
183+
writeMainClass();
184+
writeResource();
185+
BuildResult build = this.gradleBuild.build("bootJar");
186+
assertThat(build.task(":bootJar").getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
187+
Map<String, List<String>> indexedLayers;
188+
String layerToolsJar = "BOOT-INF/lib/" + JarModeLibrary.LAYER_TOOLS.getName();
189+
try (JarFile jarFile = new JarFile(new File(this.gradleBuild.getProjectDir(), "build/libs").listFiles()[0])) {
190+
assertThat(jarFile.getEntry(layerToolsJar)).isNotNull();
191+
assertThat(jarFile.getEntry("BOOT-INF/lib/alpha-1.2.3.jar")).isNotNull();
192+
assertThat(jarFile.getEntry("BOOT-INF/lib/bravo-1.2.3.jar")).isNotNull();
193+
assertThat(jarFile.getEntry("BOOT-INF/lib/commons-lang3-3.9.jar")).isNotNull();
194+
assertThat(jarFile.getEntry("BOOT-INF/lib/spring-core-5.2.5.RELEASE.jar")).isNotNull();
195+
assertThat(jarFile.getEntry("BOOT-INF/lib/spring-jcl-5.2.5.RELEASE.jar")).isNotNull();
196+
assertThat(jarFile.getEntry("BOOT-INF/lib/commons-io-2.7-SNAPSHOT.jar")).isNotNull();
197+
assertThat(jarFile.getEntry("BOOT-INF/classes/example/Main.class")).isNotNull();
198+
assertThat(jarFile.getEntry("BOOT-INF/classes/static/file.txt")).isNotNull();
199+
assertThat(jarFile.getEntry("BOOT-INF/layers.idx")).isNotNull();
200+
indexedLayers = readLayerIndex(jarFile);
201+
}
202+
List<String> layerNames = Arrays.asList("dependencies", "commons-dependencies", "snapshot-dependencies",
203+
"subproject-dependencies", "static", "app");
204+
assertThat(indexedLayers.keySet()).containsExactlyElementsOf(layerNames);
205+
Set<String> expectedSubprojectDependencies = new TreeSet<>();
206+
expectedSubprojectDependencies.add("BOOT-INF/lib/alpha-1.2.3.jar");
207+
expectedSubprojectDependencies.add("BOOT-INF/lib/bravo-1.2.3.jar");
208+
Set<String> expectedDependencies = new TreeSet<>();
209+
expectedDependencies.add("BOOT-INF/lib/spring-core-5.2.5.RELEASE.jar");
210+
expectedDependencies.add("BOOT-INF/lib/spring-jcl-5.2.5.RELEASE.jar");
211+
List<String> expectedSnapshotDependencies = new ArrayList<>();
212+
expectedSnapshotDependencies.add("BOOT-INF/lib/commons-io-2.7-SNAPSHOT.jar");
213+
(layerToolsJar.contains("SNAPSHOT") ? expectedSnapshotDependencies : expectedDependencies).add(layerToolsJar);
214+
assertThat(indexedLayers.get("subproject-dependencies"))
215+
.containsExactlyElementsOf(expectedSubprojectDependencies);
216+
assertThat(indexedLayers.get("dependencies")).containsExactlyElementsOf(expectedDependencies);
217+
assertThat(indexedLayers.get("commons-dependencies")).containsExactly("BOOT-INF/lib/commons-lang3-3.9.jar");
218+
assertThat(indexedLayers.get("snapshot-dependencies")).containsExactlyElementsOf(expectedSnapshotDependencies);
219+
assertThat(indexedLayers.get("static")).containsExactly("BOOT-INF/classes/static/");
220+
List<String> appLayer = new ArrayList<>(indexedLayers.get("app"));
221+
Set<String> nonLoaderEntries = new TreeSet<>();
222+
nonLoaderEntries.add("BOOT-INF/classes/example/");
223+
nonLoaderEntries.add("BOOT-INF/classpath.idx");
224+
nonLoaderEntries.add("BOOT-INF/layers.idx");
225+
nonLoaderEntries.add("META-INF/");
226+
assertThat(appLayer).containsSubsequence(nonLoaderEntries);
227+
appLayer.removeAll(nonLoaderEntries);
228+
assertThat(appLayer).containsExactly("org/");
229+
BuildResult listLayers = this.gradleBuild.build("listLayers");
230+
assertThat(listLayers.task(":listLayers").getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
231+
String listLayersOutput = listLayers.getOutput();
232+
assertThat(new BufferedReader(new StringReader(listLayersOutput)).lines()).containsSequence(layerNames);
233+
BuildResult extractLayers = this.gradleBuild.build("extractLayers");
234+
assertThat(extractLayers.task(":extractLayers").getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
235+
assertExtractedLayers(layerNames, indexedLayers);
236+
}
237+
179238
private void assertExtractedLayers(List<String> layerNames, Map<String, List<String>> indexedLayers)
180239
throws IOException {
181240
Map<String, List<String>> extractedLayers = readExtractedLayers(this.gradleBuild.getProjectDir(), layerNames);
@@ -201,6 +260,16 @@ private boolean isInIndex(List<String> index, String file) {
201260
return false;
202261
}
203262

263+
private void writeSettingsGradle() {
264+
try (PrintWriter writer = new PrintWriter(
265+
new FileWriter(new File(this.gradleBuild.getProjectDir(), "settings.gradle")))) {
266+
writer.println("include 'alpha', 'bravo'");
267+
}
268+
catch (IOException ex) {
269+
throw new RuntimeException(ex);
270+
}
271+
}
272+
204273
private void writeMainClass() {
205274
File examplePackage = new File(this.gradleBuild.getProjectDir(), "src/main/java/example");
206275
examplePackage.mkdirs();

spring-boot-project/spring-boot-tools/spring-boot-gradle-plugin/src/test/java/org/springframework/boot/gradle/tasks/bundling/BootJarTests.java

Lines changed: 62 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,14 @@
3030
import java.util.zip.ZipEntry;
3131

3232
import org.gradle.api.Action;
33-
import org.gradle.api.artifacts.ArtifactCollection;
3433
import org.gradle.api.artifacts.Configuration;
35-
import org.gradle.api.artifacts.ResolvableDependencies;
34+
import org.gradle.api.artifacts.ModuleVersionIdentifier;
35+
import org.gradle.api.artifacts.ResolvedArtifact;
36+
import org.gradle.api.artifacts.ResolvedConfiguration;
37+
import org.gradle.api.artifacts.ResolvedModuleVersion;
3638
import org.gradle.api.artifacts.component.ComponentArtifactIdentifier;
3739
import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
38-
import org.gradle.api.artifacts.result.ResolvedArtifactResult;
40+
import org.gradle.api.artifacts.component.ProjectComponentIdentifier;
3941
import org.junit.jupiter.api.Test;
4042

4143
import org.springframework.boot.gradle.tasks.bundling.BootJarTests.TestBootJar;
@@ -51,6 +53,7 @@
5153
* @author Andy Wilkinson
5254
* @author Madhura Bhave
5355
* @author Scott Frederick
56+
* @author Paddy Drury
5457
*/
5558
class BootJarTests extends AbstractBootArchiveTests<TestBootJar> {
5659

@@ -99,22 +102,26 @@ void whenJarIsLayeredThenLayersIndexIsPresentAndCorrect() throws IOException {
99102
try (JarFile jarFile = new JarFile(createLayeredJar())) {
100103
List<String> entryNames = getEntryNames(jarFile);
101104
assertThat(entryNames).contains("BOOT-INF/lib/first-library.jar", "BOOT-INF/lib/second-library.jar",
102-
"BOOT-INF/lib/third-library-SNAPSHOT.jar", "BOOT-INF/classes/com/example/Application.class",
103-
"BOOT-INF/classes/application.properties", "BOOT-INF/classes/static/test.css");
105+
"BOOT-INF/lib/third-library-SNAPSHOT.jar", "BOOT-INF/lib/first-project-library.jar",
106+
"BOOT-INF/lib/second-project-library-SNAPSHOT.jar",
107+
"BOOT-INF/classes/com/example/Application.class", "BOOT-INF/classes/application.properties",
108+
"BOOT-INF/classes/static/test.css");
104109
List<String> index = entryLines(jarFile, "BOOT-INF/layers.idx");
105110
assertThat(getLayerNames(index)).containsExactly("dependencies", "spring-boot-loader",
106111
"snapshot-dependencies", "application");
107112
String layerToolsJar = "BOOT-INF/lib/" + JarModeLibrary.LAYER_TOOLS.getName();
108113
List<String> expected = new ArrayList<>();
109114
expected.add("- \"dependencies\":");
110115
expected.add(" - \"BOOT-INF/lib/first-library.jar\"");
116+
expected.add(" - \"BOOT-INF/lib/first-project-library.jar\"");
111117
expected.add(" - \"BOOT-INF/lib/second-library.jar\"");
112118
if (!layerToolsJar.contains("SNAPSHOT")) {
113119
expected.add(" - \"" + layerToolsJar + "\"");
114120
}
115121
expected.add("- \"spring-boot-loader\":");
116122
expected.add(" - \"org/\"");
117123
expected.add("- \"snapshot-dependencies\":");
124+
expected.add(" - \"BOOT-INF/lib/second-project-library-SNAPSHOT.jar\"");
118125
if (layerToolsJar.contains("SNAPSHOT")) {
119126
expected.add(" - \"" + layerToolsJar + "\"");
120127
}
@@ -145,8 +152,10 @@ void whenJarIsLayeredWithCustomStrategiesThenLayersIndexIsPresentAndCorrect() th
145152
try (JarFile jarFile = new JarFile(jar)) {
146153
List<String> entryNames = getEntryNames(jar);
147154
assertThat(entryNames).contains("BOOT-INF/lib/first-library.jar", "BOOT-INF/lib/second-library.jar",
148-
"BOOT-INF/lib/third-library-SNAPSHOT.jar", "BOOT-INF/classes/com/example/Application.class",
149-
"BOOT-INF/classes/application.properties", "BOOT-INF/classes/static/test.css");
155+
"BOOT-INF/lib/third-library-SNAPSHOT.jar", "BOOT-INF/lib/first-project-library.jar",
156+
"BOOT-INF/lib/second-project-library-SNAPSHOT.jar",
157+
"BOOT-INF/classes/com/example/Application.class", "BOOT-INF/classes/application.properties",
158+
"BOOT-INF/classes/static/test.css");
150159
List<String> index = entryLines(jarFile, "BOOT-INF/layers.idx");
151160
assertThat(getLayerNames(index)).containsExactly("my-deps", "my-internal-deps", "my-snapshot-deps",
152161
"resources", "application");
@@ -156,8 +165,10 @@ void whenJarIsLayeredWithCustomStrategiesThenLayersIndexIsPresentAndCorrect() th
156165
expected.add(" - \"" + layerToolsJar + "\"");
157166
expected.add("- \"my-internal-deps\":");
158167
expected.add(" - \"BOOT-INF/lib/first-library.jar\"");
168+
expected.add(" - \"BOOT-INF/lib/first-project-library.jar\"");
159169
expected.add(" - \"BOOT-INF/lib/second-library.jar\"");
160170
expected.add("- \"my-snapshot-deps\":");
171+
expected.add(" - \"BOOT-INF/lib/second-project-library-SNAPSHOT.jar\"");
161172
expected.add(" - \"BOOT-INF/lib/third-library-SNAPSHOT.jar\"");
162173
expected.add("- \"resources\":");
163174
expected.add(" - \"BOOT-INF/classes/static/\"");
@@ -179,14 +190,19 @@ void jarsInLibAreStored() throws IOException {
179190
assertThat(jarFile.getEntry("BOOT-INF/lib/second-library.jar").getMethod()).isEqualTo(ZipEntry.STORED);
180191
assertThat(jarFile.getEntry("BOOT-INF/lib/third-library-SNAPSHOT.jar").getMethod())
181192
.isEqualTo(ZipEntry.STORED);
193+
assertThat(jarFile.getEntry("BOOT-INF/lib/first-project-library.jar").getMethod())
194+
.isEqualTo(ZipEntry.STORED);
195+
assertThat(jarFile.getEntry("BOOT-INF/lib/second-project-library-SNAPSHOT.jar").getMethod())
196+
.isEqualTo(ZipEntry.STORED);
182197
}
183198
}
184199

185200
@Test
186201
void whenJarIsLayeredClasspathIndexPointsToLayeredLibs() throws IOException {
187202
try (JarFile jarFile = new JarFile(createLayeredJar())) {
188203
assertThat(entryLines(jarFile, "BOOT-INF/classpath.idx")).containsExactly("- \"first-library.jar\"",
189-
"- \"second-library.jar\"", "- \"third-library-SNAPSHOT.jar\"");
204+
"- \"second-library.jar\"", "- \"third-library-SNAPSHOT.jar\"", "- \"first-project-library.jar\"",
205+
"- \"second-project-library-SNAPSHOT.jar\"");
190206
}
191207
}
192208

@@ -209,7 +225,8 @@ void classpathIndexPointsToBootInfLibs() throws IOException {
209225
assertThat(jarFile.getManifest().getMainAttributes().getValue("Spring-Boot-Classpath-Index"))
210226
.isEqualTo("BOOT-INF/classpath.idx");
211227
assertThat(entryLines(jarFile, "BOOT-INF/classpath.idx")).containsExactly("- \"first-library.jar\"",
212-
"- \"second-library.jar\"", "- \"third-library-SNAPSHOT.jar\"");
228+
"- \"second-library.jar\"", "- \"third-library-SNAPSHOT.jar\"", "- \"first-project-library.jar\"",
229+
"- \"second-project-library-SNAPSHOT.jar\"");
213230
}
214231
}
215232

@@ -251,34 +268,55 @@ private void addContent() throws IOException {
251268
File css = new File(staticResources, "test.css");
252269
css.createNewFile();
253270
bootJar.classpath(classesJavaMain, resourcesMain, jarFile("first-library.jar"), jarFile("second-library.jar"),
254-
jarFile("third-library-SNAPSHOT.jar"));
255-
Set<ResolvedArtifactResult> artifacts = new LinkedHashSet<>();
271+
jarFile("third-library-SNAPSHOT.jar"), jarFile("first-project-library.jar"),
272+
jarFile("second-project-library-SNAPSHOT.jar"));
273+
Set<ResolvedArtifact> artifacts = new LinkedHashSet<>();
256274
artifacts.add(mockLibraryArtifact("first-library.jar", "com.example", "first-library", "1.0.0"));
257275
artifacts.add(mockLibraryArtifact("second-library.jar", "com.example", "second-library", "1.0.0"));
258276
artifacts.add(
259277
mockLibraryArtifact("third-library-SNAPSHOT.jar", "com.example", "third-library", "1.0.0.SNAPSHOT"));
260-
ArtifactCollection resolvedDependencies = mock(ArtifactCollection.class);
261-
given(resolvedDependencies.getArtifacts()).willReturn(artifacts);
262-
ResolvableDependencies resolvableDependencies = mock(ResolvableDependencies.class);
263-
given(resolvableDependencies.getArtifacts()).willReturn(resolvedDependencies);
278+
artifacts
279+
.add(mockProjectArtifact("first-project-library.jar", "com.example", "first-project-library", "1.0.0"));
280+
artifacts.add(mockProjectArtifact("second-project-library-SNAPSHOT.jar", "com.example",
281+
"second-project-library", "1.0.0.SNAPSHOT"));
282+
ResolvedConfiguration resolvedConfiguration = mock(ResolvedConfiguration.class);
283+
given(resolvedConfiguration.getResolvedArtifacts()).willReturn(artifacts);
264284
Configuration configuration = mock(Configuration.class);
265285
given(configuration.isCanBeResolved()).willReturn(true);
266-
given(configuration.getIncoming()).willReturn(resolvableDependencies);
286+
given(configuration.getResolvedConfiguration()).willReturn(resolvedConfiguration);
267287
bootJar.setConfiguration(Collections.singleton(configuration));
268288
}
269289

270-
private ResolvedArtifactResult mockLibraryArtifact(String fileName, String group, String module, String version) {
271-
ModuleComponentIdentifier identifier = mock(ModuleComponentIdentifier.class);
272-
given(identifier.getGroup()).willReturn(group);
273-
given(identifier.getModule()).willReturn(module);
274-
given(identifier.getVersion()).willReturn(version);
290+
private ResolvedArtifact mockLibraryArtifact(String fileName, String group, String module, String version) {
291+
ModuleComponentIdentifier moduleComponentIdentifier = mock(ModuleComponentIdentifier.class);
275292
ComponentArtifactIdentifier libraryArtifactId = mock(ComponentArtifactIdentifier.class);
276-
given(libraryArtifactId.getComponentIdentifier()).willReturn(identifier);
277-
ResolvedArtifactResult libraryArtifact = mock(ResolvedArtifactResult.class);
293+
given(libraryArtifactId.getComponentIdentifier()).willReturn(moduleComponentIdentifier);
294+
ResolvedArtifact libraryArtifact = mockArtifact(fileName, group, module, version);
295+
given(libraryArtifact.getId()).willReturn(libraryArtifactId);
296+
return libraryArtifact;
297+
}
298+
299+
private ResolvedArtifact mockProjectArtifact(String fileName, String group, String module, String version) {
300+
ProjectComponentIdentifier projectComponentIdentifier = mock(ProjectComponentIdentifier.class);
301+
ComponentArtifactIdentifier projectArtifactId = mock(ComponentArtifactIdentifier.class);
302+
given(projectArtifactId.getComponentIdentifier()).willReturn(projectComponentIdentifier);
303+
ResolvedArtifact projectArtifact = mockArtifact(fileName, group, module, version);
304+
given(projectArtifact.getId()).willReturn(projectArtifactId);
305+
return projectArtifact;
306+
}
307+
308+
private ResolvedArtifact mockArtifact(String fileName, String group, String module, String version) {
309+
ModuleVersionIdentifier moduleVersionIdentifier = mock(ModuleVersionIdentifier.class);
310+
given(moduleVersionIdentifier.getGroup()).willReturn(group);
311+
given(moduleVersionIdentifier.getName()).willReturn(module);
312+
given(moduleVersionIdentifier.getVersion()).willReturn(version);
313+
ResolvedModuleVersion moduleVersion = mock(ResolvedModuleVersion.class);
314+
given(moduleVersion.getId()).willReturn(moduleVersionIdentifier);
315+
ResolvedArtifact libraryArtifact = mock(ResolvedArtifact.class);
278316
File file = new File(this.temp, fileName).getAbsoluteFile();
279317
System.out.println(file);
280318
given(libraryArtifact.getFile()).willReturn(file);
281-
given(libraryArtifact.getId()).willReturn(libraryArtifactId);
319+
given(libraryArtifact.getModuleVersion()).willReturn(moduleVersion);
282320
return libraryArtifact;
283321
}
284322

0 commit comments

Comments
 (0)