From 4447610d02b3da6daeadc79fdc0f1c16cf95dc00 Mon Sep 17 00:00:00 2001 From: Geert Bevin Date: Sun, 12 Jul 2026 22:14:37 -0400 Subject: [PATCH] Parallelize update checks and wrapper extension resolution through new ParallelDependencyResolver --- .../bld/dependencies/DependencyResolver.java | 41 ------ .../bld/dependencies/DependencyScopes.java | 2 +- .../rife/bld/dependencies/DependencySet.java | 2 +- .../ParallelDependencyResolver.java | 118 ++++++++++++++++++ .../rife/bld/operations/UpdatesOperation.java | 26 ++-- .../bld/wrapper/WrapperExtensionResolver.java | 5 +- 6 files changed, 141 insertions(+), 53 deletions(-) create mode 100644 src/main/java/rife/bld/dependencies/ParallelDependencyResolver.java diff --git a/src/main/java/rife/bld/dependencies/DependencyResolver.java b/src/main/java/rife/bld/dependencies/DependencyResolver.java index d9c0c1c..5400505 100644 --- a/src/main/java/rife/bld/dependencies/DependencyResolver.java +++ b/src/main/java/rife/bld/dependencies/DependencyResolver.java @@ -9,7 +9,6 @@ import rife.tools.exceptions.FileUtilsErrorException; import java.io.*; import java.util.*; -import java.util.function.Supplier; import java.util.stream.Collectors; import static rife.bld.dependencies.Dependency.*; @@ -180,46 +179,6 @@ public class DependencyResolver { return result; } - /** - * Resolves the transitive dependencies of multiple root dependencies, - * merging the results in the order of the provided roots. - *

- * The roots are resolved in parallel according to the resolution - * parallelism, while a shared {@code PomPrefetcher} speculatively warms - * the retriever cache across all of them. The merged result is identical - * to resolving each root sequentially. - * - * @param resolution the version resolution state that can be cached - * @param retriever the retriever to use to get artifacts - * @param repositories the repositories to use for the resolution - * @param roots the root dependencies to resolve - * @param scopes the scopes to return the transitive dependencies for - * @return the merged transitive dependencies of all the roots - * @since 2.3.1 - */ - static DependencySet resolveAllDependencies(VersionResolution resolution, ArtifactRetriever retriever, List repositories, Collection roots, Scope... scopes) { - var result = new DependencySet(); - if (roots.isEmpty()) { - return result; - } - - var prefetcher = PomPrefetcher.create(resolution, retriever, repositories); - try { - var resolutions = new ArrayList>(roots.size()); - for (var root : roots) { - resolutions.add(() -> new DependencyResolver(resolution, retriever, repositories, root).getAllDependencies(prefetcher, scopes)); - } - for (var dependencies : ParallelExecution.execute(resolutions, resolution.resolutionParallelism())) { - result.addAll(dependencies); - } - } finally { - if (prefetcher != null) { - prefetcher.shutdown(); - } - } - return result; - } - private boolean matchesExclusions(Dependency context, PomDependency checked) { while (context != null) { if (context.exclusions() != null) { diff --git a/src/main/java/rife/bld/dependencies/DependencyScopes.java b/src/main/java/rife/bld/dependencies/DependencyScopes.java index 7f0c8f2..d93e277 100644 --- a/src/main/java/rife/bld/dependencies/DependencyScopes.java +++ b/src/main/java/rife/bld/dependencies/DependencyScopes.java @@ -155,7 +155,7 @@ public class DependencyScopes extends LinkedHashMap { roots.addAll(scoped_dependencies); } } - var dependencies = DependencyResolver.resolveAllDependencies(resolution, retriever, repositories, roots, transitiveScopes); + var dependencies = new ParallelDependencyResolver(resolution, retriever, repositories).resolveAllDependencies(roots, transitiveScopes); if (excluded != null) { dependencies.removeAll(excluded); } diff --git a/src/main/java/rife/bld/dependencies/DependencySet.java b/src/main/java/rife/bld/dependencies/DependencySet.java index a62bd10..47749b2 100644 --- a/src/main/java/rife/bld/dependencies/DependencySet.java +++ b/src/main/java/rife/bld/dependencies/DependencySet.java @@ -226,7 +226,7 @@ public class DependencySet extends AbstractSet implements Set repositories, Scope... scopes) { - return DependencyResolver.resolveAllDependencies(resolution, retriever, repositories, this, scopes).generateDependencyTree(); + return new ParallelDependencyResolver(resolution, retriever, repositories).resolveAllDependencies(this, scopes).generateDependencyTree(); } /** diff --git a/src/main/java/rife/bld/dependencies/ParallelDependencyResolver.java b/src/main/java/rife/bld/dependencies/ParallelDependencyResolver.java new file mode 100644 index 0000000..76ee196 --- /dev/null +++ b/src/main/java/rife/bld/dependencies/ParallelDependencyResolver.java @@ -0,0 +1,118 @@ +/* + * Copyright 2001-2026 Geert Bevin (gbevin[remove] at uwyn dot com) + * Licensed under the Apache License, Version 2.0 (the "License") + */ +package rife.bld.dependencies; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.function.Supplier; + +/** + * Resolves multiple dependencies in parallel within a list of + * Maven-compatible repositories. + *

+ * The parallelism is determined by {@link VersionResolution#resolutionParallelism()}, + * setting it to {@code 1} makes the resolution sequential. The results are + * always identical to resolving each dependency sequentially with a + * {@link DependencyResolver}, in the same order. + * + * @author Geert Bevin (gbevin[remove] at uwyn dot com) + * @since 2.3.1 + */ +public class ParallelDependencyResolver { + private final VersionResolution resolution_; + private final ArtifactRetriever retriever_; + private final List repositories_; + + /** + * Creates a new parallel resolver. + *

+ * The repositories will be checked in the order they're listed. + * + * @param resolution the version resolution state that can be cached + * @param retriever the retriever to use to get artifacts + * @param repositories the repositories to use for the resolution + * @since 2.3.1 + */ + public ParallelDependencyResolver(VersionResolution resolution, ArtifactRetriever retriever, List repositories) { + resolution_ = resolution; + retriever_ = retriever; + if (repositories == null) { + repositories = List.of(); + } + repositories_ = repositories; + } + + /** + * Resolves the transitive dependencies of multiple root dependencies, + * merging the results in the order of the provided roots. + *

+ * The roots are resolved in parallel while a shared prefetcher + * speculatively warms the retriever cache across all of them. + * + * @param roots the root dependencies to resolve + * @param scopes the scopes to return the transitive dependencies for + * @return the merged transitive dependencies of all the roots + * @since 2.3.1 + */ + public DependencySet resolveAllDependencies(Collection roots, Scope... scopes) { + var result = new DependencySet(); + if (roots.isEmpty()) { + return result; + } + + var prefetcher = PomPrefetcher.create(resolution_, retriever_, repositories_); + try { + var resolutions = new ArrayList>(roots.size()); + for (var root : roots) { + resolutions.add(() -> new DependencyResolver(resolution_, retriever_, repositories_, root).getAllDependencies(prefetcher, scopes)); + } + for (var dependencies : ParallelExecution.execute(resolutions, resolution_.resolutionParallelism())) { + result.addAll(dependencies); + } + } finally { + if (prefetcher != null) { + prefetcher.shutdown(); + } + } + return result; + } + + /** + * Resolves the latest versions of multiple dependencies, returning them + * in the same order as the provided dependencies. + * + * @param dependencies the dependencies to resolve the latest versions of + * @return the latest versions in the order of the provided dependencies + * @since 2.3.1 + */ + public List resolveLatestVersions(List dependencies) { + var resolutions = new ArrayList>(dependencies.size()); + for (var dependency : dependencies) { + resolutions.add(() -> new DependencyResolver(resolution_, retriever_, repositories_, dependency).latestVersion()); + } + return ParallelExecution.execute(resolutions, resolution_.resolutionParallelism()); + } + + /** + * Returns the version resolution state that can be cached. + * + * @return the version resolution state + * @since 2.3.1 + */ + public VersionResolution resolution() { + return resolution_; + } + + /** + * Retrieve the repositories that are used by this resolver. + * + * @return the resolver's repositories + * @since 2.3.1 + */ + public List repositories() { + return repositories_; + } +} diff --git a/src/main/java/rife/bld/operations/UpdatesOperation.java b/src/main/java/rife/bld/operations/UpdatesOperation.java index 43da649..67634b6 100644 --- a/src/main/java/rife/bld/operations/UpdatesOperation.java +++ b/src/main/java/rife/bld/operations/UpdatesOperation.java @@ -31,16 +31,26 @@ public class UpdatesOperation extends AbstractOperation { */ public void execute() { var resolution = new VersionResolution(properties()); - var result = new DependencyScopes(); + + var scopes = new ArrayList(); + var dependencies = new ArrayList(); for (var entry : dependencies_.entrySet()) { - var scope = entry.getKey(); for (var dependency : entry.getValue()) { - var latest = new DependencyResolver(resolution, artifactRetriever(), repositories(), dependency).latestVersion(); - if (latest.compareTo(dependency.version()) > 0) { - var latest_dependency = new Dependency(dependency.groupId(), dependency.artifactId(), latest, - dependency.classifier(), dependency.type()); - result.scope(scope).include(latest_dependency); - } + scopes.add(entry.getKey()); + dependencies.add(dependency); + } + } + + var latest_versions = new ParallelDependencyResolver(resolution, artifactRetriever(), repositories()).resolveLatestVersions(dependencies); + + var result = new DependencyScopes(); + for (var i = 0; i < dependencies.size(); ++i) { + var dependency = dependencies.get(i); + var latest = latest_versions.get(i); + if (latest.compareTo(dependency.version()) > 0) { + var latest_dependency = new Dependency(dependency.groupId(), dependency.artifactId(), latest, + dependency.classifier(), dependency.type()); + result.scope(scopes.get(i)).include(latest_dependency); } } diff --git a/src/main/java/rife/bld/wrapper/WrapperExtensionResolver.java b/src/main/java/rife/bld/wrapper/WrapperExtensionResolver.java index af5671a..c87e03d 100644 --- a/src/main/java/rife/bld/wrapper/WrapperExtensionResolver.java +++ b/src/main/java/rife/bld/wrapper/WrapperExtensionResolver.java @@ -89,12 +89,13 @@ public class WrapperExtensionResolver { private Set transferExtensionDependencies() { var filenames = new HashSet(); - var dependencies = new DependencySet(); + var roots = new ArrayList(); for (var d : dependencies_) { if (d != null) { - dependencies.addAll(new DependencyResolver(resolution_, retriever_, repositories_, d).getAllDependencies(Scope.compile, Scope.runtime)); + roots.add(d); } } + var dependencies = new ParallelDependencyResolver(resolution_, retriever_, repositories_).resolveAllDependencies(roots, Scope.compile, Scope.runtime); if (!dependencies.isEmpty()) { ensurePrintedHeader();