This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new 966288a74b25 CAMEL-26901: camel-jbang - camel run resolves the
dependencies it is given together, with the breadth-first collector (#29782)
966288a74b25 is described below
commit 966288a74b25d16f9924bd8bf8e34184e0e20942
Author: Claus Ibsen <[email protected]>
AuthorDate: Sat Nov 14 21:57:51 2026 +0100
CAMEL-26901: camel-jbang - camel run resolves the dependencies it is given
together, with the breadth-first collector (#29782)
- 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.
CAMEL-26901: camel-tooling-maven - the test of the defaults accepts what a
system property configures, as the CI build does
Co-Authored-By: Claude Opus 5.5 <[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 | 19 +++
11 files changed, 321 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 f8d465a3ff3c..dfa094cf759e 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
@@ -2778,6 +2778,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 e2dba168bb6b..71e34598309c 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-jbang-running.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-jbang-running.adoc
@@ -368,6 +368,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 09d2fede6848..dce124883c24 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) {
@@ -115,24 +131,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 25872b3a698f..bf2f43e9d5a0 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
@@ -304,7 +304,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);
}
@@ -327,22 +364,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
@@ -350,8 +389,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
@@ -366,9 +407,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) {
@@ -396,8 +437,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()) {
@@ -408,7 +451,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 71eceabb26c2..3276530dd15e 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;
@@ -145,6 +146,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 13e0141c04ee..30a3d5bda2e3 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;
@@ -948,6 +960,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 11e6e486c946..d252d5469f0c 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
@@ -29,6 +29,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;
@@ -1251,4 +1252,22 @@ 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();
+ assertCamelDefault("60000", config,
"aether.connector.requestTimeout");
+ assertCamelDefault("bf", config,
"aether.dependencyCollector.impl");
+ assertCamelDefault("10", config,
"aether.dependencyCollector.bf.threads");
+ }
+ }
+
+ // a system property wins over the default of Camel (the CI build sets
aether.connector.requestTimeout=300000)
+ private static void assertCamelDefault(String expected, Map<String,
Object> config, String key) {
+ assertEquals(System.getProperty(key, expected),
String.valueOf(config.get(key)), key);
+ }
}