Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parallel version updates #1207

Merged
merged 1 commit into from
Jan 3, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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();
Expand All @@ -66,6 +79,8 @@ public abstract class UseLatestVersionsMojoBase extends AbstractVersionsDependen

protected abstract Optional<ArtifactVersion> versionProducer(Stream<ArtifactVersion> stream);

private final ExecutorService executor = Executors.newFixedThreadPool(numThreads);

public UseLatestVersionsMojoBase(
ArtifactHandlerManager artifactHandlerManager,
RepositorySystem repositorySystem,
Expand All @@ -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
Expand All @@ -89,74 +162,96 @@ protected void update(MutableXMLStreamReader pom)

Optional<Segment> unchangedSegment = SegmentUtils.determineUnchangedSegment(
getAllowMajorUpdates(), getAllowMinorUpdates(), getAllowIncrementalUpdates(), getLog());
ConcurrentLinkedQueue<DependencyVersionChange> versionChanges = new ConcurrentLinkedQueue<>();
Collection<CompletableFuture<Void>> 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<Void> getUpdates(
ConcurrentLinkedQueue<DependencyVersionChange> updates,
Collection<Dependency> dependencies,
DependencyChangeRecord.ChangeKind changeKind,
Optional<Segment> 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<Segment> 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<ArtifactVersion> 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));
}
}
Loading