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");
+            }
+        }
+    }
+}