This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch CAMEL-26901-batch in repository https://gitbox.apache.org/repos/asf/camel.git
commit 8b7855fe4316122afc325e3a455b858398614f03 Author: Claus Ibsen <[email protected]> AuthorDate: Thu Nov 12 23:03:56 2026 +0100 CAMEL-26901: camel-jbang - camel run resolves the dependencies it is given together, with the breadth-first collector - The dependencies given with --dep or camel.jbang.dependencies are resolved together, as one dependency graph (DependencyDownloader.downloadDependencies). When they cannot be resolved together, they are resolved one at a time, so only the one that cannot be downloaded fails (or is logged when exporting). - camel-tooling-maven uses the breadth-first dependency collector of Maven Resolver with 10 threads, which downloads the POMs in parallel; it resolves the same dependencies as the depth-first default. The aether.dependencyCollector.impl and aether.dependencyCollector.bf.threads system properties still win. Cold camel run with camel:groovy, jsonpath, kafka, http and aws2-s3: 8.4-9.2s instead of 12.8-13.8s, with the same 65 JARs on the classpath. camel export writes the same project. Co-Authored-By: Claude Opus 5.5 <[email protected]> Signed-off-by: Claus Ibsen <[email protected]> --- .../ROOT/pages/camel-4x-upgrade-guide-4_24.adoc | 8 ++ .../modules/ROOT/pages/camel-jbang-running.adoc | 5 + .../download/CommandLineDependencyDownloader.java | 9 +- .../camel/main/download/DependencyDownloader.java | 21 +++ .../camel/main/download/DownloadThreadPool.java | 54 +++++--- .../main/download/MavenDependencyDownloader.java | 71 ++++++++-- .../CommandLineDependencyDownloaderTest.java | 4 + .../download/DependencyDownloaderStrategyTest.java | 4 + .../MavenDependencyDownloaderBatchTest.java | 145 +++++++++++++++++++++ .../camel/tooling/maven/MavenDownloaderImpl.java | 24 +++- .../tooling/maven/MavenDownloaderImplTest.java | 14 ++ 11 files changed, 316 insertions(+), 43 deletions(-) diff --git a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc index 5b5aadf1caf3..5d7e07d47c96 100644 --- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc +++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc @@ -2528,6 +2528,14 @@ stops sending no longer hangs the download for that long. Set the `aether.connec (in millis) for a longer timeout. A repository that cannot be connected to is now logged with a WARN and not used for the rest of the run. +=== camel-jbang - dependencies are resolved with the breadth-first collector of Maven Resolver + +The Camel CLI (and other tools that download with `camel-tooling-maven`) now resolves dependencies with the +breadth-first collector of Maven Resolver (`aether.dependencyCollector.impl=bf`), which downloads the POMs in parallel, +instead of the depth-first one Maven uses by default. It resolves the same dependencies; a cold `camel run` with five +components downloads them about a third faster. Set the `aether.dependencyCollector.impl=df` system property to go back. +The dependencies given with `--dep` or `camel.jbang.dependencies` are now resolved together, as one dependency graph. + === camel-pqc - KeyLifecycleManager behaviour aligned across the implementations Four differences between the `KeyLifecycleManager` implementations are resolved. All four are corrections, but each diff --git a/docs/user-manual/modules/ROOT/pages/camel-jbang-running.adoc b/docs/user-manual/modules/ROOT/pages/camel-jbang-running.adoc index 6b6ea089b531..8944717e5ab4 100644 --- a/docs/user-manual/modules/ROOT/pages/camel-jbang-running.adoc +++ b/docs/user-manual/modules/ROOT/pages/camel-jbang-running.adoc @@ -348,6 +348,11 @@ To disable automatic downloading: camel run foo.java --download=false ---- +The dependencies given with `--dep` or `camel.jbang.dependencies` are resolved together, as one dependency graph, and +Camel CLI resolves with the breadth-first collector of Maven Resolver, which downloads the POMs in parallel (10 +threads). Set the `aether.dependencyCollector.impl=df` system property to use the depth-first collector of Maven +instead. + A repository that cannot be connected to (the host does not resolve, refuses the connection, or does not answer within the connect timeout of 10 seconds) is logged once with a WARN and not used for the rest of the run, so each download does not wait for it again. A repository that stops sending data fails the download after 60 seconds. diff --git a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/CommandLineDependencyDownloader.java b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/CommandLineDependencyDownloader.java index 04a8d21adff7..736c9fdb0d70 100644 --- a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/CommandLineDependencyDownloader.java +++ b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/CommandLineDependencyDownloader.java @@ -61,13 +61,8 @@ public class CommandLineDependencyDownloader extends ServiceSupport { } if (!gavs.isEmpty()) { - for (MavenGav gav : gavs) { - if (required) { - downloader.downloadRequiredDependency(gav.getGroupId(), gav.getArtifactId(), gav.getVersion()); - } else { - downloader.downloadDependency(gav.getGroupId(), gav.getArtifactId(), gav.getVersion()); - } - } + // together, so they are downloaded in parallel + downloader.downloadDependencies(gavs, required); } } diff --git a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/DependencyDownloader.java b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/DependencyDownloader.java index ad118a5a2deb..3217abeca445 100644 --- a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/DependencyDownloader.java +++ b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/DependencyDownloader.java @@ -23,6 +23,7 @@ import org.apache.camel.CamelContextAware; import org.apache.camel.StaticService; import org.apache.camel.tooling.maven.MavenArtifact; import org.apache.camel.tooling.maven.MavenArtifactDescriptor; +import org.apache.camel.tooling.maven.MavenGav; import org.apache.camel.tooling.maven.RepositoryResolver; /** @@ -170,6 +171,26 @@ public interface DependencyDownloader extends CamelContextAware, StaticService { */ void downloadRequiredDependency(String groupId, String artifactId, String version); + /** + * Downloads the dependencies incl transitive dependencies, resolved together as one dependency graph, so their JARs + * are downloaded in parallel. When they cannot be downloaded together, they are downloaded one at a time. + * + * @param gavs the dependencies + * @param required whether a dependency that cannot be downloaded fails, as + * {@link #downloadRequiredDependency(String, String, String)}, instead of logging the + * error + * @throws DownloadException when required and a dependency cannot be downloaded + */ + default void downloadDependencies(List<MavenGav> gavs, boolean required) { + for (MavenGav gav : gavs) { + if (required) { + downloadRequiredDependency(gav.getGroupId(), gav.getArtifactId(), gav.getVersion()); + } else { + downloadDependency(gav.getGroupId(), gav.getArtifactId(), gav.getVersion()); + } + } + } + /** * Downloads as hidden dependency that are not captured as a requirement * diff --git a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/DownloadThreadPool.java b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/DownloadThreadPool.java index 1e399a62e496..faecbd73d034 100644 --- a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/DownloadThreadPool.java +++ b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/DownloadThreadPool.java @@ -16,6 +16,7 @@ */ package org.apache.camel.main.download; +import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -75,15 +76,30 @@ class DownloadThreadPool extends ServiceSupport implements CamelContextAware { * error */ public void download(Logger log, Runnable task, String gav, boolean required) { + download(log, task, List.of(gav), required); + } + + /** + * Downloads dependencies together, and waits for the download to complete. + * + * @param required whether to throw a {@link DownloadException} when the download fails, instead of logging the + * error + */ + public void download(Logger log, Runnable task, List<String> gavs, boolean required) { Future<?> future = executorService.submit(task); - awaitCompletion(log, future, gav, required); + awaitCompletion(log, future, gavs, required); } void awaitCompletion(Logger log, Future<?> future, String gav) { - awaitCompletion(log, future, gav, false); + awaitCompletion(log, future, List.of(gav), false); } void awaitCompletion(Logger log, Future<?> future, String gav, boolean required) { + awaitCompletion(log, future, List.of(gav), required); + } + + void awaitCompletion(Logger log, Future<?> future, List<String> gavs, boolean required) { + String gav = String.join(", ", gavs); StopWatch watch = new StopWatch(); boolean done = false; while (!done) { @@ -113,24 +129,26 @@ class DownloadThreadPool extends ServiceSupport implements CamelContextAware { } } - MavenGav a = MavenGav.parseGav(gav); - DownloadRecord downloadRecord = downloader.getDownloadState(a.getGroupId(), a.getArtifactId(), a.getVersion()); - if (downloadRecord != null) { - long taken = watch.taken(); - String url = downloadRecord.repoUrl(); - String id = downloadRecord.repoId(); - String msg = "Downloaded: " + gav + " (took: " - + TimeUtils.printDuration(taken, true) + ") from: " + id + "@" + url; - log.info(msg); - } else { - long taken = watch.taken(); - String msg = "Resolved: " + gav + " (took: " - + TimeUtils.printDuration(taken, true) + ")"; - if (verbose || taken > 2000) { - // slow resolving then log + long taken = watch.taken(); + for (String g : gavs) { + MavenGav a = MavenGav.parseGav(g); + DownloadRecord downloadRecord + = downloader.getDownloadState(a.getGroupId(), a.getArtifactId(), a.getVersion()); + if (downloadRecord != null) { + String url = downloadRecord.repoUrl(); + String id = downloadRecord.repoId(); + String msg = "Downloaded: " + g + " (took: " + + TimeUtils.printDuration(taken, true) + ") from: " + id + "@" + url; log.info(msg); } else { - log.debug(msg); + String msg = "Resolved: " + g + " (took: " + + TimeUtils.printDuration(taken, true) + ")"; + if (verbose || taken > 2000) { + // slow resolving then log + log.info(msg); + } else { + log.debug(msg); + } } } } diff --git a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/MavenDependencyDownloader.java b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/MavenDependencyDownloader.java index 215cd1f0fe8c..8749c51a6483 100644 --- a/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/MavenDependencyDownloader.java +++ b/dsl/camel-kamelet-main/src/main/java/org/apache/camel/main/download/MavenDependencyDownloader.java @@ -303,7 +303,44 @@ public class MavenDependencyDownloader extends ServiceSupport implements Depende String parentGav, String groupId, String artifactId, String version, boolean transitively, boolean hidden, String extraRepos, boolean required) { + MavenGav gav = prepare(groupId, artifactId, version, hidden); + if (gav != null) { + doDownload(parentGav, List.of(gav), transitively, extraRepos, required); + } + } + + @Override + public void downloadDependencies(List<MavenGav> gavs, boolean required) { + List<MavenGav> todo = new ArrayList<>(); + for (MavenGav gav : gavs) { + MavenGav prepared = prepare(gav.getGroupId(), gav.getArtifactId(), gav.getVersion(), false); + if (prepared != null) { + todo.add(prepared); + } + } + if (todo.size() > 1) { + try { + // together: one dependency graph, and the JARs are downloaded in parallel + doDownload(null, todo, true, null, true); + return; + } catch (DownloadException e) { + // one at a time, so only the one that cannot be downloaded fails (or is logged) + LOG.debug("Cannot download the dependencies together, downloading them one at a time: {}", + e.getMessage()); + } + } + for (MavenGav gav : todo) { + doDownload(null, List.of(gav), true, null, required); + } + } + /** + * Prepares a dependency to download: resolves its version and triggers the listeners. + * + * @return the dependency to download, or null when there is nothing to download (it is on the classpath, or has no + * version) + */ + private MavenGav prepare(String groupId, String artifactId, String version, boolean hidden) { if (versionResolver != null && version != null) { version = versionResolver.resolve(version); } @@ -326,22 +363,24 @@ public class MavenDependencyDownloader extends ServiceSupport implements Depende } if (bootClasspath.contains(target)) { // already on classpath - return; + return null; } } // we need version to be able to download from maven if (ObjectHelper.isEmpty(version)) { - return; + return null; } + return MavenGav.fromCoordinates(groupId, artifactId, version, null, null); + } - String gav = groupId + ":" + artifactId + ":" + version; - String targetVersion = version; + private void doDownload( + String parentGav, List<MavenGav> gavs, boolean transitively, String extraRepos, boolean required) { + List<String> deps = gavs.stream().map(MavenGav::toString).toList(); threadPool.download(LOG, () -> { - List<String> deps = List.of(gav); - // include Apache snapshot to make it easy to use upcoming releases - boolean useApacheSnapshots = "org.apache.camel".equals(groupId) && targetVersion.contains("SNAPSHOT"); + boolean useApacheSnapshots = gavs.stream() + .anyMatch(g -> "org.apache.camel".equals(g.getGroupId()) && g.getVersion().contains("SNAPSHOT")); // include extra repositories (if any) - these will be used in addition // to the ones detected from ~/.m2/settings.xml and configured in @@ -349,8 +388,10 @@ public class MavenDependencyDownloader extends ServiceSupport implements Depende Set<String> extraRepositories = new LinkedHashSet<>(resolveExtraRepositories(extraRepos)); if (knownReposResolver != null) { // and from known extra repositories (if any) - String known = knownReposResolver.getRepo(artifactId); - extraRepositories.addAll(resolveExtraRepositories(known)); + for (MavenGav g : gavs) { + String known = knownReposResolver.getRepo(g.getArtifactId()); + extraRepositories.addAll(resolveExtraRepositories(known)); + } } // what is already on the classpath is left out (with its dependencies), so it is not downloaded @@ -365,9 +406,9 @@ public class MavenDependencyDownloader extends ServiceSupport implements Depende } List<File> files = new ArrayList<>(); if (verbose) { - LOG.info("Dependencies: {} -> [{}]", gav, artifacts); + LOG.info("Dependencies: {} -> [{}]", String.join(", ", deps), artifacts); } else { - LOG.debug("Dependencies: {} -> [{}]", gav, artifacts); + LOG.debug("Dependencies: {} -> [{}]", String.join(", ", deps), artifacts); } for (MavenArtifact a : artifacts) { @@ -395,8 +436,10 @@ public class MavenDependencyDownloader extends ServiceSupport implements Depende } } if (!artifacts.isEmpty()) { - for (DownloadListener listener : downloadListeners) { - listener.onDownloadedDependency(groupId, artifactId, targetVersion); + for (MavenGav g : gavs) { + for (DownloadListener listener : downloadListeners) { + listener.onDownloadedDependency(g.getGroupId(), g.getArtifactId(), g.getVersion()); + } } } if (!extraRepositories.isEmpty()) { @@ -407,7 +450,7 @@ public class MavenDependencyDownloader extends ServiceSupport implements Depende } } - }, gav, required); + }, deps, required); } @Override diff --git a/dsl/camel-kamelet-main/src/test/java/org/apache/camel/main/download/CommandLineDependencyDownloaderTest.java b/dsl/camel-kamelet-main/src/test/java/org/apache/camel/main/download/CommandLineDependencyDownloaderTest.java index 556dce5ef25c..3fd667d94d7c 100644 --- a/dsl/camel-kamelet-main/src/test/java/org/apache/camel/main/download/CommandLineDependencyDownloaderTest.java +++ b/dsl/camel-kamelet-main/src/test/java/org/apache/camel/main/download/CommandLineDependencyDownloaderTest.java @@ -16,6 +16,7 @@ */ package org.apache.camel.main.download; +import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.List; @@ -111,6 +112,9 @@ class CommandLineDependencyDownloaderTest { downloaded.add(args[0] + ":" + args[1] + ":" + args[2]); return null; } + case "downloadDependencies" -> { + return InvocationHandler.invokeDefault(proxy, method, args); + } case "downloadRequiredDependency" -> { downloaded.add("required:" + args[0] + ":" + args[1] + ":" + args[2]); if (requiredFailure != null) { diff --git a/dsl/camel-kamelet-main/src/test/java/org/apache/camel/main/download/DependencyDownloaderStrategyTest.java b/dsl/camel-kamelet-main/src/test/java/org/apache/camel/main/download/DependencyDownloaderStrategyTest.java index 5a8a222df17e..da6fe714ce99 100644 --- a/dsl/camel-kamelet-main/src/test/java/org/apache/camel/main/download/DependencyDownloaderStrategyTest.java +++ b/dsl/camel-kamelet-main/src/test/java/org/apache/camel/main/download/DependencyDownloaderStrategyTest.java @@ -16,6 +16,7 @@ */ package org.apache.camel.main.download; +import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.List; @@ -76,6 +77,9 @@ class DependencyDownloaderStrategyTest { downloaded.add(args[0] + ":" + args[1] + ":" + args[2]); return null; } + case "downloadDependencies" -> { + return InvocationHandler.invokeDefault(proxy, method, args); + } case "downloadRequiredDependency" -> { downloaded.add("required:" + args[0] + ":" + args[1] + ":" + args[2]); if (requiredFailure != null) { diff --git a/dsl/camel-kamelet-main/src/test/java/org/apache/camel/main/download/MavenDependencyDownloaderBatchTest.java b/dsl/camel-kamelet-main/src/test/java/org/apache/camel/main/download/MavenDependencyDownloaderBatchTest.java new file mode 100644 index 000000000000..e48a85f39b17 --- /dev/null +++ b/dsl/camel-kamelet-main/src/test/java/org/apache/camel/main/download/MavenDependencyDownloaderBatchTest.java @@ -0,0 +1,145 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * http://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 org.apache.camel.main.download; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +import org.apache.camel.tooling.maven.MavenGav; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Dependencies downloaded together, from a local file repository. + */ +class MavenDependencyDownloaderBatchTest { + + @TempDir + Path dir; + + private MavenDependencyDownloader downloader; + private DependencyDownloaderClassLoader classLoader; + private final List<String> downloaded = new ArrayList<>(); + + @BeforeEach + void setUp() throws Exception { + Path repo = Files.createDirectories(dir.resolve("repo")); + // a -> common 1.0, b -> common 2.0 + writeJar(repo, "a", "1.0", "common", "1.0"); + writeJar(repo, "b", "1.0", "common", "2.0"); + writeJar(repo, "common", "1.0", null, null); + writeJar(repo, "common", "2.0", null, null); + Path settings = Files.writeString(dir.resolve("settings.xml"), + "<settings><localRepository>" + dir.resolve("local") + "</localRepository></settings>"); + + classLoader = new DependencyDownloaderClassLoader(getClass().getClassLoader()); + downloader = new MavenDependencyDownloader(); + downloader.setClassLoader(classLoader); + downloader.setMavenCentralEnabled(false); + downloader.setMavenApacheSnapshotEnabled(false); + downloader.setMavenSettings(settings.toString()); + downloader.setRepositories(repo.toUri().toString()); + downloader.addDownloadListener(new DownloadListener() { + @Override + public void onDownloadDependency(String groupId, String artifactId, String version) { + } + + @Override + public void onDownloadedDependency(String groupId, String artifactId, String version) { + downloaded.add(groupId + ":" + artifactId + ":" + version); + } + + @Override + public void onAlreadyDownloadedDependency(String groupId, String artifactId, String version) { + } + }); + downloader.start(); + } + + @AfterEach + void tearDown() throws Exception { + downloader.stop(); + classLoader.close(); + } + + @Test + void dependenciesAreResolvedTogether() { + downloader.downloadDependencies(List.of(gav("a"), gav("b")), true); + + assertEquals(List.of("org.example.batch:a:1.0", "org.example.batch:b:1.0"), downloaded); + // one graph: one version of common (the nearest first: a's) + assertEquals(List.of("a-1.0.jar", "b-1.0.jar", "common-1.0.jar"), jars()); + } + + @Test + void dependencyThatCannotBeDownloadedIsLogged() { + downloader.downloadDependencies(List.of(gav("a"), gav("missing"), gav("b")), false); + + // one at a time: the others are downloaded + assertEquals(List.of("org.example.batch:a:1.0", "org.example.batch:b:1.0"), downloaded); + assertTrue(jars().containsAll(List.of("a-1.0.jar", "b-1.0.jar")), jars().toString()); + } + + @Test + void requiredDependencyThatCannotBeDownloadedFails() { + DownloadException e = assertThrows(DownloadException.class, + () -> downloader.downloadDependencies(List.of(gav("a"), gav("missing"), gav("b")), true)); + + // the one that cannot be downloaded + assertTrue(e.getMessage().startsWith("Cannot download dependency: org.example.batch:missing:1.0 due to: "), + e.getMessage()); + assertEquals(List.of("org.example.batch:a:1.0"), downloaded); + } + + private static MavenGav gav(String artifactId) { + return MavenGav.fromCoordinates("org.example.batch", artifactId, "1.0", null, null); + } + + private List<String> jars() { + return classLoader.getDownloaded().stream().sorted().toList(); + } + + private static void writeJar(Path repo, String artifactId, String version, String dependency, String dependencyVersion) + throws Exception { + Path dir = Files.createDirectories(repo.resolve("org/example/batch/" + artifactId + "/" + version)); + String deps = dependency == null + ? "" + : """ + <dependencies> + <dependency><groupId>org.example.batch</groupId><artifactId>%s</artifactId><version>%s</version></dependency> + </dependencies> + """ + .formatted(dependency, dependencyVersion); + Files.writeString(dir.resolve(artifactId + "-" + version + ".pom"), """ + <project xmlns="http://maven.apache.org/POM/4.0.0"> + <modelVersion>4.0.0</modelVersion> + <groupId>org.example.batch</groupId> + <artifactId>%s</artifactId> + <version>%s</version> + %s</project> + """.formatted(artifactId, version, deps)); + Files.writeString(dir.resolve(artifactId + "-" + version + ".jar"), artifactId); + } +} diff --git a/tooling/camel-tooling-maven/src/main/java/org/apache/camel/tooling/maven/MavenDownloaderImpl.java b/tooling/camel-tooling-maven/src/main/java/org/apache/camel/tooling/maven/MavenDownloaderImpl.java index d720e4544a2f..9a0e0b08f81f 100644 --- a/tooling/camel-tooling-maven/src/main/java/org/apache/camel/tooling/maven/MavenDownloaderImpl.java +++ b/tooling/camel-tooling-maven/src/main/java/org/apache/camel/tooling/maven/MavenDownloaderImpl.java @@ -124,6 +124,10 @@ public class MavenDownloaderImpl extends ServiceSupport implements MavenDownload private static final String UPDATE_CHECK_SESSION_STATE = "aether.updateCheckManager.sessionState"; private static final DependencyFilter ACCEPT_ALL = (node, parents) -> true; private static final int DEFAULT_REQUEST_TIMEOUT = 60000; + // see org.eclipse.aether.internal.impl.collect.DefaultDependencyCollector and BfDependencyCollector + private static final String COLLECTOR_IMPL = "aether.dependencyCollector.impl"; + private static final String COLLECTOR_BF_THREADS = "aether.dependencyCollector.bf.threads"; + private static final int COLLECTOR_THREADS = 10; // Configuration private boolean mavenCentralEnabled = true; @@ -373,7 +377,7 @@ public class MavenDownloaderImpl extends ServiceSupport implements MavenDownload // Get RepositorySystem and RepositorySystemSession from MIMA repositorySystem = mimaContext.repositorySystem(); - repositorySystemSession = withTimeoutsAndReachability(withTrackingKeyCompat(mimaContext.repositorySystemSession())); + repositorySystemSession = withCamelDefaults(withTrackingKeyCompat(mimaContext.repositorySystemSession())); // Configure repositories (same as embedded mode) List<RemoteRepository> originalRepositories = new ArrayList<>(); @@ -865,14 +869,22 @@ public class MavenDownloaderImpl extends ServiceSupport implements MavenDownload } /** - * Sets the request timeout of Camel (unless configured, such as with the aether.connector.requestTimeout system - * property), and stops using a repository that cannot be connected to. + * Sets the defaults of Camel (unless configured, such as with the aether.connector.requestTimeout system property): + * the request timeout, and the breadth-first dependency collector, which downloads the POMs in parallel (it + * resolves the same dependencies as the depth-first default of Maven Resolver). And stops using a repository that + * cannot be connected to. */ - private RepositorySystemSession withTimeoutsAndReachability(RepositorySystemSession session) { + private RepositorySystemSession withCamelDefaults(RepositorySystemSession session) { DefaultRepositorySystemSession answer = new DefaultRepositorySystemSession(session); if (session.getConfigProperties().get(ConfigurationProperties.REQUEST_TIMEOUT) == null) { answer.setConfigProperty(ConfigurationProperties.REQUEST_TIMEOUT, requestTimeout); } + if (session.getConfigProperties().get(COLLECTOR_IMPL) == null) { + answer.setConfigProperty(COLLECTOR_IMPL, "bf"); + } + if (session.getConfigProperties().get(COLLECTOR_BF_THREADS) == null) { + answer.setConfigProperty(COLLECTOR_BF_THREADS, COLLECTOR_THREADS); + } answer.setTransferListener(ChainedTransferListener.newInstance(session.getTransferListener(), new UnreachableRepositoryListener(unreachableRepositories))); return answer; @@ -942,6 +954,10 @@ public class MavenDownloaderImpl extends ServiceSupport implements MavenDownload return unreachableRepositories; } + RepositorySystemSession getRepositorySystemSession() { + return repositorySystemSession; + } + @Override public MavenArtifactDescriptor resolveArtifactDescriptor(String gav, Set<String> extraRepositories) throws MavenResolutionException { diff --git a/tooling/camel-tooling-maven/src/test/java/org/apache/camel/tooling/maven/MavenDownloaderImplTest.java b/tooling/camel-tooling-maven/src/test/java/org/apache/camel/tooling/maven/MavenDownloaderImplTest.java index 61a8e5568299..21c9a7bfe53f 100644 --- a/tooling/camel-tooling-maven/src/test/java/org/apache/camel/tooling/maven/MavenDownloaderImplTest.java +++ b/tooling/camel-tooling-maven/src/test/java/org/apache/camel/tooling/maven/MavenDownloaderImplTest.java @@ -28,6 +28,7 @@ import java.nio.file.Files; import java.security.MessageDigest; import java.util.Base64; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.CopyOnWriteArrayList; @@ -1244,4 +1245,17 @@ class MavenDownloaderImplTest { assertNull(MavenDownloaderImpl.UnreachableRepositoryListener.connectFailure(new IOException("Not found"))); assertNull(MavenDownloaderImpl.UnreachableRepositoryListener.connectFailure(null)); } + + @Test + void testCamelDefaults() throws Exception { + try (MavenDownloaderImpl downloader = new MavenDownloaderImpl()) { + downloader.setMavenSettingsLocation("false"); + downloader.build(); + + Map<String, Object> config = downloader.getRepositorySystemSession().getConfigProperties(); + assertEquals(60000, config.get("aether.connector.requestTimeout")); + assertEquals("bf", config.get("aether.dependencyCollector.impl")); + assertEquals(10, config.get("aether.dependencyCollector.bf.threads")); + } + } }
