You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2026/04/28 20:14:21 UTC
(camel) branch camel-4.18.x updated: CAMEL-23353: Fix + add missing doc, test and proper handle when download=false for extra.repos (#22784) (#22806)
This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch camel-4.18.x
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/camel-4.18.x by this push:
new 1b5db5b8a238 CAMEL-23353: Fix + add missing doc, test and proper handle when download=false for extra.repos (#22784) (#22806)
1b5db5b8a238 is described below
commit 1b5db5b8a2384e503eb83fa19497cf72f1671a2e
Author: JiriOndrusek <on...@gmail.com>
AuthorDate: Tue Apr 28 22:14:14 2026 +0200
CAMEL-23353: Fix + add missing doc, test and proper handle when download=false for extra.repos (#22784) (#22806)
---
.../modules/ROOT/pages/camel-jbang.adoc | 11 ++
.../dsl/jbang/core/commands/ExportQuarkus.java | 10 +-
.../camel/tooling/maven/MavenDownloaderImpl.java | 4 +-
.../tooling/maven/MavenDownloaderImplTest.java | 180 +++++++++++++++++++++
4 files changed, 199 insertions(+), 6 deletions(-)
diff --git a/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc b/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc
index cb0171670615..a4576d36a531 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc
@@ -824,6 +824,14 @@ with the key `camel.jbang.repos` as shown:
camel.jbang.repos=https://packages.atlassian.com/maven-external
----
+Alternatively, you can configure default Maven repositories globally via a system property.
+This is useful for custom Camel distributions that require additional repositories without manual configuration on each command:
+
+[source,bash]
+----
+export JAVA_TOOL_OPTIONS="-Dcamel.extra.repos=https://repo1.example.com/maven2,https://repo2.example.com/releases"
+----
+
When running Camel you need to include the properties file to use:
[source,bash]
@@ -4010,6 +4018,9 @@ camel export --runtime=quarkus --gav=com.foo:acme:1.0-SNAPSHOT --directory=../my
TIP: See the possible options by running: `camel export --help` for more details.
+NOTE: The Quarkus platform version is automatically resolved from the Quarkus platform registry based on the Camel version's compatible Quarkus stream.
+If the registry is unavailable or downloads are disabled, the build-time default version is used.
+
NOTE: You cannot use `--profile` option when exporting to Camel Quarkus.
=== Exporting to Camel Main
diff --git a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ExportQuarkus.java b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ExportQuarkus.java
index 4166a65cfc08..97061b189198 100644
--- a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ExportQuarkus.java
+++ b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ExportQuarkus.java
@@ -145,10 +145,12 @@ class ExportQuarkus extends Export {
appJar = "target" + File.separator + ids[1] + "-" + ids[2] + ".jar";
}
copyReadme(BUILD_DIR, appJar);
- // resolve Quarkus platform version from registry (if available)
- String resolved = QuarkusHelper.resolveQuarkusPlatformVersion(quarkusVersion);
- if (resolved != null) {
- quarkusVersion = resolved;
+ // resolve Quarkus platform version from registry (when download is true)
+ if (download) {
+ String resolved = QuarkusHelper.resolveQuarkusPlatformVersion(quarkusVersion);
+ if (resolved != null) {
+ quarkusVersion = resolved;
+ }
}
// gather dependencies
Set<String> deps = resolveDependencies(settings, profile);
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 5fae5299bb14..ab92d4cd0a88 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
@@ -333,6 +333,8 @@ public class MavenDownloaderImpl extends ServiceSupport implements MavenDownload
// process repositories - both from settings.xml and from --repos option. All are subject to
// mirroring and proxying (handled by org.eclipse.aether.RepositorySystem#newResolutionRepositories())
List<RemoteRepository> originalRepositories = configureDefaultRepositories(settings);
+ // Load extra default repositories (classpath properties file + system property)
+ loadExtraDefaultRepositories(originalRepositories);
remoteRepositories.addAll(repositorySystem.newResolutionRepositories(repositorySystemSession,
originalRepositories));
@@ -358,8 +360,6 @@ public class MavenDownloaderImpl extends ServiceSupport implements MavenDownload
Collections.singletonList(apacheSnapshotsRepository)).get(0);
}
- // Load extra default repositories (classpath properties file + system property)
- loadExtraDefaultRepositories(originalRepositories);
}
/**
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
new file mode 100644
index 000000000000..0979d05994fa
--- /dev/null
+++ b/tooling/camel-tooling-maven/src/test/java/org/apache/camel/tooling/maven/MavenDownloaderImplTest.java
@@ -0,0 +1,180 @@
+/*
+ * 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.tooling.maven;
+
+import java.io.File;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.security.MessageDigest;
+import java.util.Base64;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.commons.codec.binary.Hex;
+import org.apache.http.Header;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.bootstrap.HttpServer;
+import org.apache.http.impl.bootstrap.ServerBootstrap;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+/**
+ * Tests for MavenDownloaderImpl using MIMA (Minimal Maven).
+ * <p>
+ * Note: Most tests are disabled by default and require -DenableMavenDownloaderTests=true because they download from
+ * Maven Central and can be slow/flaky in CI environments.
+ */
+class MavenDownloaderImplTest {
+
+ private static final Logger LOG = LoggerFactory.getLogger(MavenDownloaderImplTest.class);
+
+ private static HttpServer localServer;
+
+ @TempDir
+ File tempDir;
+
+ @BeforeAll
+ public static void startMavenMirror() throws Exception {
+ localServer = ServerBootstrap.bootstrap()
+ .setListenerPort(AvailablePortFinder.getNextAvailable(9234, 10000))
+ .registerHandler("/maven/*", (req, res, context) -> {
+ Header authz = req.getFirstHeader("Authorization");
+ if (authz == null) {
+ res.addHeader("WWW-Authenticate", "Basic realm=Camel");
+ res.setStatusCode(401);
+ return;
+ }
+ String creds = new String(Base64.getDecoder().decode(authz.getValue().split(" ")[1]));
+ if (!"camel:passw0rd".equals(creds)) {
+ res.setStatusCode(403);
+ return;
+ }
+ LOG.info("Request: {}", req.getRequestLine());
+ String request = req.getRequestLine().getUri().substring("/maven".length());
+ if (request.endsWith(".jar") || request.endsWith(".pom")) {
+ res.setEntity(new StringEntity(request));
+ } else {
+ try {
+ MessageDigest md = null;
+ if (request.endsWith(".md5")) {
+ md = MessageDigest.getInstance("MD5");
+ request = request.substring(0, request.length() - 4);
+ } else if (request.endsWith(".sha1")) {
+ md = MessageDigest.getInstance("SHA");
+ request = request.substring(0, request.length() - 5);
+ }
+ if (md != null) {
+ byte[] digest = md.digest(request.getBytes(StandardCharsets.UTF_8));
+ res.setEntity(new StringEntity(Hex.encodeHexString(digest)));
+ }
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ res.setStatusCode(200);
+ })
+ .create();
+ localServer.start();
+ }
+
+ @AfterAll
+ public static void stopMavenMirror() {
+ if (localServer != null) {
+ localServer.shutdown(2, TimeUnit.SECONDS);
+ }
+ }
+
+ @Test
+ void testExtraDefaultRepositoriesFromSystemProperty() throws Exception {
+ // This test verifies that MIMA correctly loads extra default repositories from system properties.
+
+ // Use the local test server that requires authentication
+ String testRepoUrl = "http://localhost:" + localServer.getLocalPort() + "/maven/repository";
+
+ File customLocalRepo = new File(tempDir, "extra-repos-test-m2");
+ Files.createDirectories(customLocalRepo.toPath());
+
+ String artifactCoords = "org.apache.camel:camel-test:9.99.9-non-exist";
+
+ // Callable that captures local variables and performs artifact resolution
+ Callable<List<MavenArtifact>> resolve = () -> {
+ try (MavenDownloaderImpl downloader = new MavenDownloaderImpl()) {
+ downloader.setMavenCentralEnabled(false);
+ downloader.setMavenApacheSnapshotEnabled(false);
+ downloader.build();
+
+ MavenDownloader customDownloader = downloader.customize(
+ customLocalRepo.getAbsolutePath(),
+ 5000,
+ 10000);
+
+ return customDownloader.resolveArtifacts(
+ List.of(artifactCoords),
+ null,
+ false,
+ false);
+ }
+ };
+
+ String originalValue = System.getProperty("camel.extra.repos");
+ try {
+ // NEGATIVE TEST: without the extra repo, the resolution fails because of no artifact can be resolved, definitely not authentication failure
+ System.clearProperty("camel.extra.repos");
+
+ try {
+ resolve.call();
+ fail("Should have failed without extra repos system property");
+ } catch (MavenResolutionException e) {
+ // Expected - no repositories configured
+ assertFalse(e.getMessage().contains("401") || e.getMessage().contains("Unauthorized")
+ || e.getMessage().contains("status code"),
+ "Should fail due to no repositories, not authentication, got: " + e.getMessage());
+ }
+
+ // POSITIVE TEST: with the extra repo configured, we have to receive authentication failure
+ System.setProperty("camel.extra.repos", testRepoUrl);
+
+ try {
+ resolve.call();
+ fail("Should have failed with 401 Unauthorized (proves repo was loaded from system property)");
+ } catch (MavenResolutionException e) {
+ // Expected - repository loaded but download fails due to no authentication
+ // The 401 error proves the repository from the system property was actually used
+ assertTrue(e.getMessage().contains("401") || e.getMessage().contains("Unauthorized")
+ || e.getMessage().contains("status code"),
+ "Should fail with 401/authentication error (proves repository was used), got: " + e.getMessage());
+ }
+
+ } finally {
+ // Clean up system property
+ if (originalValue != null) {
+ System.setProperty("camel.extra.repos", originalValue);
+ } else {
+ System.clearProperty("camel.extra.repos");
+ }
+ }
+ }
+}