Skip to content

Commit 0010855

Browse files
committed
fix: Adding a managed Maven dependency throws exception if it exists in same or higher version (#779)
* format: Fix copyright header * dep: Bumped spring-asciidoctor-backends to 0.0.5 * dep: Removed Spring release repository after brown out
1 parent 6345136 commit 0010855

File tree

8 files changed

+375
-23
lines changed

8 files changed

+375
-23
lines changed

applications/spring-shell/src/test/java/org/springframework/sbm/IntegrationTestBaseClass.java

+4-4
Original file line numberDiff line numberDiff line change
@@ -178,23 +178,23 @@ protected Path getTestDir() {
178178
* @param applicableRecipes
179179
*/
180180
protected void assertApplicableRecipesContain(String... applicableRecipes) {
181-
List<String> recipeNames = getRecipeNames();
181+
List<String> recipeNames = getApplicableRecipeNames();
182182
assertThat(recipeNames).contains(applicableRecipes);
183183
}
184184

185185
@NotNull
186-
private List<String> getRecipeNames() {
186+
protected List<String> getApplicableRecipeNames() {
187187
return applicableRecipeListCommand.execute(projectContextHolder.getProjectContext()).stream()
188188
.map(r -> r.getName()).collect(Collectors.toList());
189189
}
190190

191191
protected void assertRecipeApplicable(String recipeName) {
192-
List<String> recipeNames = getRecipeNames();
192+
List<String> recipeNames = getApplicableRecipeNames();
193193
assertThat(recipeNames).contains(recipeName);
194194
}
195195

196196
protected void assertRecipeNotApplicable(String recipeName) {
197-
List<String> recipeNames = getRecipeNames();
197+
List<String> recipeNames = getApplicableRecipeNames();
198198
assertThat(recipeNames).doesNotContain(recipeName);
199199
}
200200

components/sbm-core/src/main/java/org/springframework/sbm/build/migration/actions/AddMavenDependencyManagementAction.java

+49-2
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,16 @@
1515
*/
1616
package org.springframework.sbm.build.migration.actions;
1717

18-
import org.springframework.sbm.build.api.Module;
18+
import org.apache.maven.artifact.versioning.ComparableVersion;
19+
import org.jetbrains.annotations.NotNull;
1920
import org.springframework.sbm.build.api.BuildFile;
2021
import org.springframework.sbm.build.api.Dependency;
2122
import org.springframework.sbm.engine.recipe.AbstractAction;
2223
import org.springframework.sbm.engine.context.ProjectContext;
2324
import lombok.Setter;
2425

26+
import java.util.Optional;
27+
2528
@Setter
2629
public class AddMavenDependencyManagementAction extends AbstractAction {
2730

@@ -33,14 +36,58 @@ public class AddMavenDependencyManagementAction extends AbstractAction {
3336

3437
@Override
3538
public void apply(ProjectContext context) {
39+
verifyNoConflictingManagedDependencyExists(context);
40+
3641
Dependency dependency = Dependency.builder()
3742
.groupId(groupId)
3843
.artifactId(artifactId)
3944
.version(version)
4045
.scope(scope)
4146
.type(dependencyType)
4247
.build();
48+
BuildFile rootBuildFile = context.getApplicationModules().getRootModule().getBuildFile();
49+
rootBuildFile.addToDependencyManagement(dependency);
50+
}
51+
52+
@NotNull
53+
private void verifyNoConflictingManagedDependencyExists(ProjectContext context) {
54+
BuildFile rootBuildFile = context.getApplicationModules().getRootModule().getBuildFile();
55+
Optional<Dependency> managedSpringDep = rootBuildFile
56+
.getRequestedDependencyManagement()
57+
.stream()
58+
.filter(this::matchingDependencyManagementSection)
59+
.findFirst();
60+
61+
if(managedSpringDep.isPresent()) {
62+
Dependency managedDep = managedSpringDep.get();
63+
int comparisonResult = compareVersions(this.version, managedDep.getVersion());
64+
if(managedDependencyHasSameVersion(comparisonResult) || managedDependencyHasHigherVersion(comparisonResult)) {
65+
String message = String.format(
66+
"Failed to add a managed dependency %s with version %s. This managed dependency already exists in %s in version %s.",
67+
this.groupId + ":" + this.artifactId,
68+
this.version,
69+
rootBuildFile.getAbsolutePath(),
70+
managedDep.getVersion()
71+
);
72+
throw new IllegalStateException(message);
73+
}
74+
}
75+
}
76+
77+
private boolean managedDependencyHasSameVersion(int comparisonResult) {
78+
return comparisonResult == 0;
79+
}
80+
81+
private boolean managedDependencyHasHigherVersion(int comparisonResult) {
82+
return comparisonResult == -1;
83+
}
84+
85+
private int compareVersions(String newVersion, String existingVersion) {
86+
return new ComparableVersion(newVersion).compareTo(new ComparableVersion(existingVersion));
87+
}
4388

44-
context.getApplicationModules().getRootModule().getBuildFile().addToDependencyManagement(dependency);
89+
private boolean matchingDependencyManagementSection(Dependency dependency) {
90+
return dependency.getGroupId().equals(groupId) &&
91+
dependency.getArtifactId().equals(artifactId);
4592
}
4693
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*
2+
* Copyright 2021 - 2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.sbm.build.migration.conditions;
17+
18+
import lombok.Setter;
19+
import org.apache.maven.artifact.versioning.ComparableVersion;
20+
import org.springframework.sbm.build.api.BuildFile;
21+
import org.springframework.sbm.build.api.Dependency;
22+
import org.springframework.sbm.build.api.Module;
23+
import org.springframework.sbm.engine.context.ProjectContext;
24+
import org.springframework.sbm.engine.recipe.Condition;
25+
26+
import java.util.List;
27+
28+
/**
29+
* @author Fabian Krüger
30+
*/
31+
@Setter
32+
public class NoMoreRecentManagedDependencyExists implements Condition {
33+
34+
private String groupId;
35+
private String artifactId;
36+
private String version;
37+
38+
39+
@Override
40+
public String getDescription() {
41+
return "Check that no more recent managed dependency exists";
42+
}
43+
44+
@Override
45+
public boolean evaluate(ProjectContext context) {
46+
return context.getApplicationModules().stream().map(Module::getBuildFile)
47+
.noneMatch(this::hasConflictingManagedDependency);
48+
}
49+
50+
private boolean hasConflictingManagedDependency(BuildFile buildFile) {
51+
List<Dependency> requestedDependencyManagement = buildFile.getRequestedDependencyManagement();
52+
if(requestedDependencyManagement == null || requestedDependencyManagement.isEmpty()) {
53+
return false;
54+
}
55+
return buildFile.getRequestedDependencyManagement().stream()
56+
.anyMatch(this::isConflictingDEpendency);
57+
}
58+
59+
private boolean isConflictingDEpendency(Dependency dependency) {
60+
boolean matchingGA = groupId.equals(dependency.getGroupId()) &&
61+
artifactId.equals(dependency.getArtifactId());
62+
63+
if(matchingGA) {
64+
return hasConflictingVersion(dependency);
65+
} else {
66+
return false;
67+
}
68+
}
69+
70+
private boolean hasConflictingVersion(Dependency dependency) {
71+
int comparisonResult = compareVersions(version, dependency.getVersion());
72+
return managedDependencyHasHigherVersion(comparisonResult) || managedDependencyHasSameVersion(comparisonResult);
73+
}
74+
75+
private boolean managedDependencyHasSameVersion(int comparisonResult) {
76+
return comparisonResult == 0;
77+
}
78+
79+
private boolean managedDependencyHasHigherVersion(int comparisonResult) {
80+
return comparisonResult == -1;
81+
}
82+
83+
private int compareVersions(String newVersion, String existingVersion) {
84+
return new ComparableVersion(newVersion).compareTo(new ComparableVersion(existingVersion));
85+
}
86+
}

components/sbm-core/src/main/java/org/springframework/sbm/java/exceptions/UnresolvedTypeException.java

+15
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,18 @@
1+
/*
2+
* Copyright 2021 - 2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
116
package org.springframework.sbm.java.exceptions;
217

318
public class UnresolvedTypeException extends RuntimeException {

components/sbm-core/src/test/java/org/springframework/sbm/build/migration/actions/AddMavenDependencyManagementActionTest.java

+57
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.springframework.sbm.testhelper.common.utils.TestDiff;
2424

2525
import static org.assertj.core.api.Assertions.assertThat;
26+
import static org.junit.jupiter.api.Assertions.assertThrows;
2627

2728
class AddMavenDependencyManagementActionTest {
2829

@@ -205,4 +206,60 @@ void shouldAddToRootPomInSingleModuleProject() {
205206
.as(TestDiff.of(buildFile.print(), expected))
206207
.isEqualTo(expected);
207208
}
209+
210+
@Test
211+
void shouldThrowExceptionWhenSameManagedDependencyExistsWithHigherVersion() {
212+
String versionInPom = "2.7.5";
213+
String versionInRecipe = "2.7.4";
214+
testAddMavenDependencyManagementAction(versionInPom, versionInRecipe);
215+
}
216+
217+
@Test
218+
void shouldThrowExceptionWhenSameManagedDependencyExistsWithSameVersion() {
219+
String versionInPom = "2.7.5";
220+
String versionInRecipe = "2.7.5";
221+
testAddMavenDependencyManagementAction(versionInPom, versionInRecipe);
222+
}
223+
224+
private void testAddMavenDependencyManagementAction(String versionInPom, String versionInRecipe) {
225+
String pom = """
226+
<?xml version="1.0" encoding="UTF-8"?>
227+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
228+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
229+
<modelVersion>4.0.0</modelVersion>
230+
<groupId>com.example</groupId>
231+
<artifactId>demo</artifactId>
232+
<version>0.0.1-SNAPSHOT</version>
233+
<name>demo</name>
234+
<description>Demo project for Spring Boot</description>
235+
<properties>
236+
<java.version>17</java.version>
237+
</properties>
238+
<dependencyManagement>
239+
<dependencies>
240+
<dependency>
241+
<groupId>org.springframework.boot</groupId>
242+
<artifactId>spring-boot-dependencies</artifactId>
243+
<version>%s</version>
244+
<type>pom</type>
245+
<scope>import</scope>
246+
</dependency>
247+
</dependencies>
248+
</dependencyManagement>
249+
</project>
250+
""".formatted(versionInPom);
251+
252+
ProjectContext projectContext = TestProjectContext.buildProjectContext().withMavenRootBuildFileSource(pom).build();
253+
254+
AddMavenDependencyManagementAction sut = new AddMavenDependencyManagementAction();
255+
sut.setGroupId("org.springframework.boot");
256+
sut.setArtifactId("spring-boot-dependencies");
257+
258+
sut.setVersion(versionInRecipe);
259+
260+
assertThrows(IllegalStateException.class, () -> {
261+
sut.apply(projectContext);
262+
}).getMessage().equals("Failed to add a managed dependency org.springframework.boot:spring-boot-dependencies with version "+ versionInRecipe +". " +
263+
"This managed dependency already exists in " + projectContext.getApplicationModules().getRootModule().getBuildFile().getAbsolutePath().toString() + " in version "+versionInPom+".");
264+
}
208265
}

0 commit comments

Comments
 (0)