diff --git a/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/UseLatestVersionsMojoBase.java b/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/UseLatestVersionsMojoBase.java index 6597d1077..f52e9f714 100644 --- a/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/UseLatestVersionsMojoBase.java +++ b/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/UseLatestVersionsMojoBase.java @@ -18,10 +18,15 @@ import javax.xml.stream.XMLStreamException; import java.io.IOException; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Map; import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import java.util.stream.Stream; import org.apache.maven.artifact.Artifact; @@ -31,6 +36,7 @@ import org.apache.maven.model.DependencyManagement; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.wagon.Wagon; import org.codehaus.mojo.versions.api.ArtifactVersions; import org.codehaus.mojo.versions.api.PomHelper; @@ -52,6 +58,13 @@ */ public abstract class UseLatestVersionsMojoBase extends AbstractVersionsDependencyUpdaterMojo { + /** + * Number of executor threads for update retrieval. + * @since 2.19.0 + */ + @Parameter(property = "numThreads", defaultValue = "5") + private int numThreads = 5; + protected abstract boolean getAllowMajorUpdates(); protected abstract boolean getAllowMinorUpdates(); @@ -66,6 +79,8 @@ public abstract class UseLatestVersionsMojoBase extends AbstractVersionsDependen protected abstract Optional versionProducer(Stream stream); + private final ExecutorService executor = Executors.newFixedThreadPool(numThreads); + public UseLatestVersionsMojoBase( ArtifactHandlerManager artifactHandlerManager, RepositorySystem repositorySystem, @@ -74,6 +89,64 @@ public UseLatestVersionsMojoBase( super(artifactHandlerManager, repositorySystem, wagonMap, changeRecorders); } + /** + * Represents a version change of an artifact + */ + private abstract static class ArtifactVersionChange { + private final DependencyChangeRecord.ChangeKind changeKind; + private final String newVersion; + + /** + * Creates a new instance + * @param changeKind change kind + * @param newVersion new version + */ + ArtifactVersionChange(DependencyChangeRecord.ChangeKind changeKind, String newVersion) { + this.changeKind = changeKind; + this.newVersion = newVersion; + } + + /** + * @return change kind + */ + DependencyChangeRecord.ChangeKind getChangeKind() { + return changeKind; + } + + /** + * @return new version + */ + String getNewVersion() { + return newVersion; + } + } + + /** + * Represents a version change of a dependency + */ + private static final class DependencyVersionChange extends ArtifactVersionChange { + private final Dependency dependency; + + /** + * Constructs a new instance + * @param changeKind change kind + * @param dependency {@code Dependency} instance + * @param newVersion new version + */ + DependencyVersionChange( + DependencyChangeRecord.ChangeKind changeKind, Dependency dependency, String newVersion) { + super(changeKind, newVersion); + this.dependency = dependency; + } + + /** + * @return {@code Dependency} instance + */ + Dependency getDependency() { + return dependency; + } + } + /** * @param pom the pom to update. * @throws org.apache.maven.plugin.MojoExecutionException when things go wrong @@ -89,74 +162,96 @@ protected void update(MutableXMLStreamReader pom) Optional unchangedSegment = SegmentUtils.determineUnchangedSegment( getAllowMajorUpdates(), getAllowMinorUpdates(), getAllowIncrementalUpdates(), getLog()); + ConcurrentLinkedQueue versionChanges = new ConcurrentLinkedQueue<>(); + Collection> versionChangeFutures = new ArrayList<>(); try { if (getProcessDependencyManagement()) { DependencyManagement dependencyManagement = PomHelper.getRawModel(getProject()).getDependencyManagement(); if (dependencyManagement != null) { - update( - pom, + versionChangeFutures.add(getUpdates( + versionChanges, dependencyManagement.getDependencies(), DependencyChangeRecord.ChangeKind.DEPENDENCY_MANAGEMENT, - unchangedSegment); + unchangedSegment)); } } if (getProject().getDependencies() != null && getProcessDependencies()) { - update( - pom, + versionChangeFutures.add(getUpdates( + versionChanges, getProject().getDependencies(), DependencyChangeRecord.ChangeKind.DEPENDENCY, - unchangedSegment); + unchangedSegment)); } if (getProject().getParent() != null && getProcessParent()) { - update( - pom, + versionChangeFutures.add(getUpdates( + versionChanges, singletonList(getParentDependency()), DependencyChangeRecord.ChangeKind.PARENT, - unchangedSegment); + unchangedSegment)); + } + + CompletableFuture.allOf(versionChangeFutures.toArray(new CompletableFuture[0])) + .join(); + for (DependencyVersionChange change : versionChanges) { + updateDependencyVersion(pom, change.getDependency(), change.getNewVersion(), change.getChangeKind()); } } catch (IOException e) { throw new MojoExecutionException(e.getMessage(), e); + } catch (IllegalStateException e) { + if (e.getCause() instanceof MojoExecutionException) { + throw (MojoExecutionException) e.getCause(); + } + if (e.getCause() instanceof VersionRetrievalException) { + throw (VersionRetrievalException) e.getCause(); + } + throw e; } } - protected final void update( - MutableXMLStreamReader pom, + private CompletableFuture getUpdates( + ConcurrentLinkedQueue updates, Collection dependencies, DependencyChangeRecord.ChangeKind changeKind, - Optional unchangedSegment) - throws XMLStreamException, MojoExecutionException, VersionRetrievalException { - for (Dependency dep : dependencies) { - if (!updateFilter(dep)) { - continue; - } else if (getExcludeReactor() && isProducedByReactor(dep)) { - getLog().info("Ignoring reactor dependency: " + toString(dep)); - continue; - } else if (isHandledByProperty(dep)) { - getLog().debug("Ignoring dependency with property as version: " + toString(dep)); - continue; - } - Artifact artifact = toArtifact(dep); - if (!isIncluded(artifact)) { - continue; - } - if (getLog().isDebugEnabled()) { - ArtifactVersion selectedVersion = DefaultArtifactVersionCache.of(dep.getVersion()); - getLog().debug("Selected version:" + selectedVersion); - getLog().debug("Looking for newer versions of " + toString(dep)); - } + Optional unchangedSegment) { + return CompletableFuture.allOf(dependencies.stream() + .map(dep -> CompletableFuture.runAsync( + () -> { + if (!updateFilter(dep)) { + return; + } else if (getExcludeReactor() && isProducedByReactor(dep)) { + getLog().info("Ignoring reactor dependency: " + toString(dep)); + } else if (isHandledByProperty(dep)) { + getLog().debug("Ignoring dependency with property as version: " + toString(dep)); + } else { + try { + Artifact artifact = toArtifact(dep); + if (!isIncluded(artifact)) { + return; + } else if (getLog().isDebugEnabled()) { + ArtifactVersion selectedVersion = + DefaultArtifactVersionCache.of(dep.getVersion()); + getLog().debug("Selected version:" + selectedVersion); + getLog().debug("Looking for newer versions of " + toString(dep)); + } - ArtifactVersions versions = getHelper().lookupArtifactVersions(artifact, false); - try { - Optional newestVer = versionProducer(Arrays.stream(versions.getNewerVersions( - dep.getVersion(), unchangedSegment, getAllowSnapshots(), getAllowDowngrade())) - .filter(this::artifactVersionsFilter)); - if (newestVer.isPresent()) { - updateDependencyVersion(pom, dep, newestVer.get().toString(), changeKind); - } - } catch (InvalidSegmentException e) { - getLog().warn("Ignoring " + this.toString(dep) + " as the version number is too short"); - } - } + ArtifactVersions versions = getHelper().lookupArtifactVersions(artifact, false); + versionProducer(Arrays.stream(versions.getNewerVersions( + dep.getVersion(), + unchangedSegment, + getAllowSnapshots(), + getAllowDowngrade())) + .filter(this::artifactVersionsFilter)) + .ifPresent(ver -> updates.add( + new DependencyVersionChange(changeKind, dep, ver.toString()))); + } catch (VersionRetrievalException + | InvalidSegmentException + | MojoExecutionException e) { + throw new IllegalStateException(e); + } + } + }, + executor)) + .toArray(CompletableFuture[]::new)); } }