diff --git a/initializr-parent/pom.xml b/initializr-parent/pom.xml
index dedf202c..4cb0ca76 100644
--- a/initializr-parent/pom.xml
+++ b/initializr-parent/pom.xml
@@ -17,8 +17,8 @@
17
1.23.0
1.10.0
+ 3.9.2
1.9.7
- 3.9.2
3.1.1
4.0.2
@@ -40,10 +40,15 @@
commons-text
${commons-text.version}
+
+ org.apache.maven
+ maven-core
+ ${maven.version}
+
org.apache.maven
maven-resolver-provider
- ${maven-resolver-provider.version}
+ ${maven.version}
org.apache.maven.resolver
diff --git a/initializr-version-resolver/pom.xml b/initializr-version-resolver/pom.xml
index 7c5d68e0..cb435803 100644
--- a/initializr-version-resolver/pom.xml
+++ b/initializr-version-resolver/pom.xml
@@ -21,6 +21,10 @@
+
+ org.apache.maven
+ maven-core
+
org.apache.maven
maven-resolver-provider
diff --git a/initializr-version-resolver/src/main/java/io/spring/initializr/versionresolver/MavenResolverDependencyManagementVersionResolver.java b/initializr-version-resolver/src/main/java/io/spring/initializr/versionresolver/DefaultMavenVersionResolver.java
similarity index 57%
rename from initializr-version-resolver/src/main/java/io/spring/initializr/versionresolver/MavenResolverDependencyManagementVersionResolver.java
rename to initializr-version-resolver/src/main/java/io/spring/initializr/versionresolver/DefaultMavenVersionResolver.java
index 562863c0..510248be 100644
--- a/initializr-version-resolver/src/main/java/io/spring/initializr/versionresolver/MavenResolverDependencyManagementVersionResolver.java
+++ b/initializr-version-resolver/src/main/java/io/spring/initializr/versionresolver/DefaultMavenVersionResolver.java
@@ -22,14 +22,24 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import org.apache.maven.model.Model;
+import org.apache.maven.model.building.DefaultModelBuilder;
+import org.apache.maven.model.building.DefaultModelBuilderFactory;
+import org.apache.maven.model.building.DefaultModelBuildingRequest;
+import org.apache.maven.model.building.ModelBuildingException;
+import org.apache.maven.model.resolution.ModelResolver;
+import org.apache.maven.project.ProjectBuildingRequest;
+import org.apache.maven.project.ProjectModelResolver;
import org.apache.maven.repository.internal.MavenRepositorySystemUtils;
import org.eclipse.aether.DefaultRepositorySystemSession;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.RepositorySystemSession;
+import org.eclipse.aether.RequestTrace;
import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory;
import org.eclipse.aether.graph.Dependency;
import org.eclipse.aether.impl.DefaultServiceLocator;
+import org.eclipse.aether.impl.RemoteRepositoryManager;
import org.eclipse.aether.internal.impl.DefaultRepositorySystem;
import org.eclipse.aether.repository.LocalRepository;
import org.eclipse.aether.repository.LocalRepositoryManager;
@@ -37,6 +47,9 @@ import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.aether.resolution.ArtifactDescriptorException;
import org.eclipse.aether.resolution.ArtifactDescriptorRequest;
import org.eclipse.aether.resolution.ArtifactDescriptorResult;
+import org.eclipse.aether.resolution.ArtifactRequest;
+import org.eclipse.aether.resolution.ArtifactResolutionException;
+import org.eclipse.aether.resolution.ArtifactResult;
import org.eclipse.aether.spi.connector.RepositoryConnectorFactory;
import org.eclipse.aether.spi.connector.transport.TransporterFactory;
import org.eclipse.aether.spi.locator.ServiceLocator;
@@ -44,14 +57,16 @@ import org.eclipse.aether.transport.http.HttpTransporterFactory;
import org.eclipse.aether.util.repository.SimpleArtifactDescriptorPolicy;
/**
- * A {@link DependencyManagementVersionResolver} that resolves versions using Maven
- * Resolver. Maven's default {@link LocalRepositoryManager} implementation is not
- * thread-safe. To avoid corruption of the local repository, interaction with the
- * {@link RepositorySystem} is single-threaded.
+ * A {@link MavenVersionResolver} that resolves versions using Maven Resolver. Maven's
+ * default {@link LocalRepositoryManager} implementation is not thread-safe. To avoid
+ * corruption of the local repository, interaction with the {@link RepositorySystem} is
+ * single-threaded.
*
* @author Andy Wilkinson
+ * @author Stephane Nicoll
*/
-class MavenResolverDependencyManagementVersionResolver implements DependencyManagementVersionResolver {
+@SuppressWarnings("removal")
+class DefaultMavenVersionResolver implements MavenVersionResolver, DependencyManagementVersionResolver {
private static final RemoteRepository mavenCentral = new RemoteRepository.Builder("central", "default",
"https://repo1.maven.org/maven2")
@@ -72,9 +87,11 @@ class MavenResolverDependencyManagementVersionResolver implements DependencyMana
private final RepositorySystemSession repositorySystemSession;
+ private final RemoteRepositoryManager remoteRepositoryManager;
+
private final RepositorySystem repositorySystem;
- MavenResolverDependencyManagementVersionResolver(Path cacheLocation) {
+ DefaultMavenVersionResolver(Path cacheLocation) {
ServiceLocator serviceLocator = createServiceLocator();
DefaultRepositorySystemSession session = MavenRepositorySystemUtils.newSession();
session.setArtifactDescriptorPolicy(new SimpleArtifactDescriptorPolicy(false, false));
@@ -84,10 +101,16 @@ class MavenResolverDependencyManagementVersionResolver implements DependencyMana
session.setUserProperties(System.getProperties());
session.setReadOnly();
this.repositorySystemSession = session;
+ this.remoteRepositoryManager = serviceLocator.getService(RemoteRepositoryManager.class);
}
@Override
public Map resolve(String groupId, String artifactId, String version) {
+ return resolveDependencies(groupId, artifactId, version);
+ }
+
+ @Override
+ public Map resolveDependencies(String groupId, String artifactId, String version) {
ArtifactDescriptorResult bom = resolveBom(groupId, artifactId, version);
Map managedVersions = new HashMap<>();
bom.getManagedDependencies()
@@ -98,6 +121,18 @@ class MavenResolverDependencyManagementVersionResolver implements DependencyMana
return managedVersions;
}
+ @Override
+ public Map resolvePlugins(String groupId, String artifactId, String version) {
+ Model model = buildEffectiveModel(groupId, artifactId, version);
+ Map managedPluginVersions = new HashMap<>();
+ model.getBuild()
+ .getPluginManagement()
+ .getPlugins()
+ .forEach((plugin) -> managedPluginVersions.putIfAbsent(plugin.getGroupId() + ":" + plugin.getArtifactId(),
+ plugin.getVersion()));
+ return managedPluginVersions;
+ }
+
private ArtifactDescriptorResult resolveBom(String groupId, String artifactId, String version) {
synchronized (this.monitor) {
try {
@@ -112,6 +147,40 @@ class MavenResolverDependencyManagementVersionResolver implements DependencyMana
}
}
+ private Model buildEffectiveModel(String groupId, String artifactId, String version) {
+ try {
+ ArtifactResult bom = resolvePom(groupId, artifactId, version);
+ RequestTrace requestTrace = new RequestTrace(null);
+
+ ModelResolver modelResolver = new ProjectModelResolver(this.repositorySystemSession, requestTrace,
+ this.repositorySystem, this.remoteRepositoryManager, repositories,
+ ProjectBuildingRequest.RepositoryMerging.POM_DOMINANT, null);
+ DefaultModelBuildingRequest modelBuildingRequest = new DefaultModelBuildingRequest();
+ modelBuildingRequest.setSystemProperties(System.getProperties());
+ modelBuildingRequest.setPomFile(bom.getArtifact().getFile());
+ modelBuildingRequest.setModelResolver(modelResolver);
+ DefaultModelBuilder modelBuilder = new DefaultModelBuilderFactory().newInstance();
+ return modelBuilder.build(modelBuildingRequest).getEffectiveModel();
+ }
+ catch (ModelBuildingException ex) {
+ throw new IllegalStateException(
+ "Model for '" + groupId + ":" + artifactId + ":" + version + "' could not be built", ex);
+ }
+ }
+
+ private ArtifactResult resolvePom(String groupId, String artifactId, String version) {
+ synchronized (this.monitor) {
+ try {
+ return this.repositorySystem.resolveArtifact(this.repositorySystemSession, new ArtifactRequest(
+ new DefaultArtifact(groupId, artifactId, "pom", version), repositories, null));
+ }
+ catch (ArtifactResolutionException ex) {
+ throw new IllegalStateException(
+ "Pom '" + groupId + ":" + artifactId + ":" + version + "' could not be resolved", ex);
+ }
+ }
+ }
+
private static ServiceLocator createServiceLocator() {
DefaultServiceLocator locator = MavenRepositorySystemUtils.newServiceLocator();
locator.addService(RepositorySystem.class, DefaultRepositorySystem.class);
diff --git a/initializr-version-resolver/src/main/java/io/spring/initializr/versionresolver/DependencyManagementVersionResolver.java b/initializr-version-resolver/src/main/java/io/spring/initializr/versionresolver/DependencyManagementVersionResolver.java
index f531a34e..bf19e885 100644
--- a/initializr-version-resolver/src/main/java/io/spring/initializr/versionresolver/DependencyManagementVersionResolver.java
+++ b/initializr-version-resolver/src/main/java/io/spring/initializr/versionresolver/DependencyManagementVersionResolver.java
@@ -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.
@@ -24,7 +24,9 @@ import java.util.Map;
* managed dependencies of a Maven bom. Implementations must be thread-safe.
*
* @author Andy Wilkinson
+ * @deprecated as of 0.20.0 in favor of {@link MavenVersionResolver}.
*/
+@Deprecated(since = "0.20.0", forRemoval = true)
public interface DependencyManagementVersionResolver {
/**
@@ -47,7 +49,7 @@ public interface DependencyManagementVersionResolver {
* @return the resolver
*/
static DependencyManagementVersionResolver withCacheLocation(Path location) {
- return new MavenResolverDependencyManagementVersionResolver(location);
+ return new DefaultMavenVersionResolver(location);
}
}
diff --git a/initializr-version-resolver/src/main/java/io/spring/initializr/versionresolver/MavenVersionResolver.java b/initializr-version-resolver/src/main/java/io/spring/initializr/versionresolver/MavenVersionResolver.java
new file mode 100644
index 00000000..37007b7c
--- /dev/null
+++ b/initializr-version-resolver/src/main/java/io/spring/initializr/versionresolver/MavenVersionResolver.java
@@ -0,0 +1,65 @@
+/*
+ * 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 io.spring.initializr.versionresolver;
+
+import java.nio.file.Path;
+import java.util.Map;
+
+/**
+ * A {@code MavenVersionResolver} is used to resolve the versions of managed dependencies
+ * or plugins. Implementations must be thread-safe.
+ *
+ * @author Andy Wilkinson
+ * @author Stephane Nicoll
+ */
+public interface MavenVersionResolver {
+
+ /**
+ * Resolves the versions in the managed dependencies of the bom identified by the
+ * given {@code groupId}, {@code artifactId}, and {@code version}.
+ * @param groupId bom group ID
+ * @param artifactId bom artifact ID
+ * @param version bom version
+ * @return the managed dependencies as a map of {@code groupId:artifactId} to
+ * {@code version}
+ */
+ Map resolveDependencies(String groupId, String artifactId, String version);
+
+ /**
+ * Resolves the versions in the managed plugins of the pom identified by the given
+ * {@code groupId}, {@code artifactId}, and {@code version}.
+ * @param groupId pom group ID
+ * @param artifactId pom artifact ID
+ * @param version pom version
+ * @return the managed plugins as a map of {@code groupId:artifactId} to
+ * {@code version}
+ */
+ Map resolvePlugins(String groupId, String artifactId, String version);
+
+ /**
+ * Creates a new {@code MavenVersionResolver} that uses the given {@code location} for
+ * its local cache. To avoid multiple instances attempting to write to the same
+ * location cache, callers should ensure that a unique location is used. The returned
+ * resolver can then be used concurrently by multiple threads.
+ * @param location cache location
+ * @return the resolver
+ */
+ static MavenVersionResolver withCacheLocation(Path location) {
+ return new DefaultMavenVersionResolver(location);
+ }
+
+}
diff --git a/initializr-version-resolver/src/test/java/io/spring/initializr/versionresolver/DefaultMavenVersionResolverTests.java b/initializr-version-resolver/src/test/java/io/spring/initializr/versionresolver/DefaultMavenVersionResolverTests.java
new file mode 100644
index 00000000..8d5e96f3
--- /dev/null
+++ b/initializr-version-resolver/src/test/java/io/spring/initializr/versionresolver/DefaultMavenVersionResolverTests.java
@@ -0,0 +1,107 @@
+/*
+ * 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 io.spring.initializr.versionresolver;
+
+import java.nio.file.Path;
+import java.util.Map;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
+
+/**
+ * Tests for {@link DefaultMavenVersionResolver}.
+ *
+ * @author Andy Wilkinson
+ * @author Stephane Nicoll
+ */
+class DefaultMavenVersionResolverTests {
+
+ private MavenVersionResolver resolver;
+
+ @BeforeEach
+ void createResolver(@TempDir Path temp) {
+ this.resolver = new DefaultMavenVersionResolver(temp);
+ }
+
+ @Test
+ void resolveDependenciesForSpringBoot() {
+ Map versions = this.resolver.resolveDependencies("org.springframework.boot",
+ "spring-boot-dependencies", "2.1.5.RELEASE");
+ assertThat(versions).containsEntry("org.flywaydb:flyway-core", "5.2.4");
+ }
+
+ @Test
+ void resolveDependenciesForSpringCloud() {
+ Map versions = this.resolver.resolveDependencies("org.springframework.cloud",
+ "spring-cloud-dependencies", "Greenwich.SR1");
+ assertThat(versions).containsEntry("com.netflix.ribbon:ribbon", "2.3.0");
+ }
+
+ @Test
+ void resolveDependenciesUsingMilestones() {
+ Map versions = this.resolver.resolveDependencies("org.springframework.boot",
+ "spring-boot-dependencies", "2.2.0.M3");
+ assertThat(versions).containsEntry("org.flywaydb:flyway-core", "5.2.4");
+ }
+
+ @Test
+ void resolveDependenciesUsingSnapshots() {
+ Map versions = this.resolver.resolveDependencies("org.springframework.boot",
+ "spring-boot-dependencies", "2.4.0-SNAPSHOT");
+ assertThat(versions).isNotEmpty();
+ }
+
+ @Test
+ void resolveDependenciesForNonExistentDependency() {
+ assertThatIllegalStateException()
+ .isThrownBy(() -> this.resolver.resolveDependencies("org.springframework.boot", "spring-boot-bom", "1.0"))
+ .withMessage("Bom 'org.springframework.boot:spring-boot-bom:1.0' could not be resolved");
+ }
+
+ @Test
+ void resolvePluginsForSpringBoot() {
+ Map versions = this.resolver.resolvePlugins("org.springframework.boot",
+ "spring-boot-starter-parent", "3.1.1");
+ assertThat(versions).containsEntry("org.springframework.boot:spring-boot-maven-plugin", "3.1.1");
+ }
+
+ @Test
+ void resolvePluginsUsingMilestones() {
+ Map versions = this.resolver.resolvePlugins("org.springframework.boot",
+ "spring-boot-dependencies", "2.2.0.M3");
+ assertThat(versions).containsEntry("org.springframework.boot:spring-boot-maven-plugin", "2.2.0.M3");
+ }
+
+ @Test
+ void resolvePluginsUsingSnapshots() {
+ Map versions = this.resolver.resolvePlugins("org.springframework.boot",
+ "spring-boot-dependencies", "2.4.0-SNAPSHOT");
+ assertThat(versions).isNotEmpty();
+ }
+
+ @Test
+ void resolvePluginsForNonExistentDependency() {
+ assertThatIllegalStateException()
+ .isThrownBy(() -> this.resolver.resolvePlugins("org.springframework.boot", "spring-boot-bom", "1.0"))
+ .withMessage("Pom 'org.springframework.boot:spring-boot-bom:1.0' could not be resolved");
+ }
+
+}
diff --git a/initializr-version-resolver/src/test/java/io/spring/initializr/versionresolver/MavenResolverDependencyManagementVersionResolverTests.java b/initializr-version-resolver/src/test/java/io/spring/initializr/versionresolver/MavenResolverDependencyManagementVersionResolverTests.java
deleted file mode 100644
index 3c8ae776..00000000
--- a/initializr-version-resolver/src/test/java/io/spring/initializr/versionresolver/MavenResolverDependencyManagementVersionResolverTests.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * 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 io.spring.initializr.versionresolver;
-
-import java.nio.file.Path;
-import java.util.Map;
-
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.io.TempDir;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
-
-/**
- * Tests for {@link MavenResolverDependencyManagementVersionResolver}.
- *
- * @author Andy Wilkinson
- */
-class MavenResolverDependencyManagementVersionResolverTests {
-
- private DependencyManagementVersionResolver resolver;
-
- @BeforeEach
- void createResolver(@TempDir Path temp) {
- this.resolver = new MavenResolverDependencyManagementVersionResolver(temp);
- }
-
- @Test
- void springBootDependencies() {
- Map versions = this.resolver.resolve("org.springframework.boot", "spring-boot-dependencies",
- "2.1.5.RELEASE");
- assertThat(versions).containsEntry("org.flywaydb:flyway-core", "5.2.4");
- }
-
- @Test
- void springCloudDependencies() {
- Map versions = this.resolver.resolve("org.springframework.cloud", "spring-cloud-dependencies",
- "Greenwich.SR1");
- assertThat(versions).containsEntry("com.netflix.ribbon:ribbon", "2.3.0");
- }
-
- @Test
- void milestoneBomCanBeResolved() {
- Map versions = this.resolver.resolve("org.springframework.boot", "spring-boot-dependencies",
- "2.2.0.M3");
- assertThat(versions).containsEntry("org.flywaydb:flyway-core", "5.2.4");
- }
-
- @Test
- void snapshotBomCanBeResolved() {
- Map versions = this.resolver.resolve("org.springframework.boot", "spring-boot-dependencies",
- "2.4.0-SNAPSHOT");
- assertThat(versions).isNotEmpty();
- }
-
- @Test
- void nonExistentDependency() {
- assertThatIllegalStateException()
- .isThrownBy(() -> this.resolver.resolve("org.springframework.boot", "spring-boot-bom", "1.0"))
- .withMessage("Bom 'org.springframework.boot:spring-boot-bom:1.0' could not be resolved");
- }
-
-}