ppalaga commented on code in PR #23333:
URL: https://github.com/apache/camel/pull/23333#discussion_r3275683276


##########
dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/QuarkusHelper.java:
##########
@@ -16,150 +16,577 @@
  */
 package org.apache.camel.dsl.jbang.core.common;
 
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UncheckedIOException;
+import java.math.BigInteger;
 import java.net.URI;
+import java.net.URISyntaxException;
 import java.net.http.HttpClient;
 import java.net.http.HttpRequest;
 import java.net.http.HttpResponse;
+import java.nio.file.Files;
+import java.nio.file.LinkOption;
+import java.nio.file.Path;
+import java.nio.file.attribute.FileTime;
 import java.time.Duration;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.ZoneId;
+import java.util.Comparator;
 import java.util.List;
-import java.util.Map;
+import java.util.Objects;
 import java.util.Optional;
+import java.util.Properties;
 import java.util.function.BiConsumer;
-import java.util.function.BinaryOperator;
 import java.util.function.Function;
-import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+
+import org.w3c.dom.Document;
+
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+import 
org.apache.camel.dsl.jbang.core.commands.version.VersionList.CamelAndRuntimeVersions;
+import org.apache.camel.tooling.maven.MavenArtifact;
+import org.apache.camel.tooling.maven.MavenDownloader;
+import org.apache.camel.tooling.maven.MavenGav;
+import org.apache.camel.util.json.DeserializationException;
 import org.apache.camel.util.json.JsonArray;
 import org.apache.camel.util.json.JsonObject;
 import org.apache.camel.util.json.Jsoner;
+import org.apache.maven.artifact.versioning.ComparableVersion;
+
+import static 
org.apache.camel.dsl.jbang.core.common.CamelJBangConstants.QUARKUS_EXTENSION_REGISTRY_BASE_URI;
+import static 
org.apache.camel.dsl.jbang.core.common.CamelJBangConstants.QUARKUS_GROUP_ID;
+import static 
org.apache.camel.dsl.jbang.core.common.CamelJBangConstants.QUARKUS_VERSION;
 
 /**
  * Helper for resolving Quarkus platform information from the Quarkus registry.
  */
 public final class QuarkusHelper {
 
     public static final String QUARKUS_PLATFORM_URL_PROPERTY = 
"camel.jbang.quarkus.platform.url";
-    public static final String DEFAULT_QUARKUS_PLATFORM_URL = 
RuntimeType.QUARKUS_EXTENSION_REGISTRY_BASE_URL
-                                                              + 
(RuntimeType.QUARKUS_EXTENSION_REGISTRY_BASE_URL.endsWith("/")
-                                                                      ? "" : 
"/")
-                                                              + 
"client/platforms";
 
     private QuarkusHelper() {
     }
 
-    /**
-     * Returns the Quarkus platform registry URL, honoring the system property 
{@value #QUARKUS_PLATFORM_URL_PROPERTY}
-     * if set.
-     */
-    public static String getQuarkusPlatformUrl() {
-        return System.getProperty(QUARKUS_PLATFORM_URL_PROPERTY, 
DEFAULT_QUARKUS_PLATFORM_URL);
+    public static Stream<CamelAndRuntimeVersions> listQuarkusPlatformVersions(
+            Function<MavenGav, MavenArtifact> mavenResolver,
+            boolean download,
+            String quarkusExtensionRegistryBaseUri,
+            boolean fresh) {
+        JsonArray streams = 
fetchPlatformStreams(quarkusExtensionRegistryBaseUri, download, fresh, 
registriesDir());
+        if (streams == null || streams.isEmpty()) {
+            return Stream.empty();
+        }
+        return listQuarkusPlatformVersions(streams, mavenResolver);
     }
 
-    /**
-     * Resolves the actual Quarkus platform version for each row by fetching 
the Quarkus platform registry and matching
-     * the Camel Quarkus major.minor version against stream IDs.
-     *
-     * @param rows                 the list of rows to enrich with Quarkus 
platform versions
-     * @param runtimeVersionFunc   function to extract the runtime (Camel 
Quarkus) version from a row
-     * @param quarkusVersionSetter consumer to set the resolved Quarkus 
platform version on a row
-     */
-    public static <T> void resolveQuarkusPlatformVersions(
-            List<T> rows,
-            Function<T, String> runtimeVersionFunc,
-            BiConsumer<T, String> quarkusVersionSetter) {
-
-        JsonArray streams = fetchPlatformStreams();
-        if (streams == null) {
-            return;
+    private static Path registriesDir() {
+        final Path regDir = 
CommandLineHelper.getCamelDir().resolve("quarkus-extension-registries");
+        try {
+            if (!Files.isDirectory(regDir)) {
+                Files.createDirectories(regDir);
+            }
+            return regDir
+                    .toRealPath(LinkOption.NOFOLLOW_LINKS);
+        } catch (IOException e) {
+            throw new UncheckedIOException("Could not get real path for " + 
regDir, e);
         }
+    }
 
-        // keep the row with the highest runtime version per major.minor stream
-        BinaryOperator<T> keepLatest = (a, b) -> VersionHelper.compare(
-                runtimeVersionFunc.apply(a), runtimeVersionFunc.apply(b)) >= 0 
? a : b;
-
-        Map<String, T> latestPerStream = rows.stream()
-                .filter(row -> runtimeVersionFunc.apply(row) != null)
-                .collect(Collectors.toMap(
-                        row -> 
VersionHelper.getMajorMinorVersion(runtimeVersionFunc.apply(row)),
-                        Function.identity(),
-                        keepLatest));
-
-        // match each major.minor against registry streams and set the quarkus 
version
-        latestPerStream.forEach((majorMinor, row) -> 
findStreamVersion(streams, majorMinor, "quarkus-core-version")
-                .ifPresent(version -> quarkusVersionSetter.accept(row, 
version)));
+    static Stream<CamelAndRuntimeVersions> listQuarkusPlatformVersions(
+            JsonArray streams,
+            Function<MavenGav, MavenArtifact> mavenResolver) {
+        return streams.stream()
+                .map(s -> (JsonObject) s)
+                .map(PlatformStream::of)
+                .flatMap(platformStream -> 
platformStream.platformReleases().stream())
+                .map(platformRelease -> {
+                    List<String> versions
+                            = 
platformRelease.findManagedVersions(mavenResolver, Ga.CAMEL_DIRECT, 
Ga.CAMEL_QUARKUS_DIRECT);
+                    return new CamelAndRuntimeVersions(
+                            versions.get(0), 
platformRelease.quarkusCamelBomGav.getVersion(), versions.get(1));
+                });
     }
 
     /**
-     * Resolves the Quarkus platform BOM version by matching the build-time 
version's major.minor against the Quarkus
-     * platform registry.
+     * Finds the newest Quarkus platform BOM {@code g:a:v} using the same or 
newer {@code major.minor} Camel version as
+     * the specified {@code camelVersion} by searching in Quarkus platform 
registry or returns the specified
+     * {@code buildTimeQuarkusVersion} if no compatible Platform version can 
be found.
      * <p>
      * This is used by export/run commands to query the registry for the 
correct platform BOM version instead of using
      * the build-time constant. The registry may have a newer compatible 
version.
      *
-     * @param  buildTimeVersion the build-time Quarkus version (e.g., 
"3.15.7.something")
-     * @return                  the resolved platform BOM version from the 
registry, or the original buildTimeVersion if
-     *                          resolution fails
+     * @param  camelVersion                    if specified, the value of 
{@code --camel-version} CLI parameter or the
+     *                                         Camel version of the currently 
running camel-jbang. Must not be
+     *                                         {@code null}
+     * @param  quarkusExtensionRegistryBaseUri
+     * @param  downloader                      A {@link MavenDownloader} to 
use for accessing
+     * @return                                 the resolved platform BOM 
version from the registry, or the original
+     *                                         buildTimeVersion if resolution 
fails
      */
-    public static String resolveQuarkusPlatformVersion(String 
buildTimeVersion) {
-        if (buildTimeVersion == null) {
-            return null;
+    public static QuarkusPlatformBom findQuarkusPlatformBom(
+            String camelVersion,
+            Function<MavenGav, MavenArtifact> mavenResolver,
+            boolean download,
+            String quarkusExtensionRegistryBaseUri,
+            boolean fresh) {
+        if (camelVersion == null) {
+            camelVersion = RuntimeType.main.version();
         }
 
-        JsonArray streams = fetchPlatformStreams();
-        if (streams == null) {
-            return buildTimeVersion;
+        JsonArray streams = 
fetchPlatformStreams(quarkusExtensionRegistryBaseUri, download, fresh, 
registriesDir());
+        if (streams == null || streams.isEmpty()) {
+            return null;
         }
+        Optional<QuarkusPlatformBom> resolved
+                = findPlatformBom(streams, new MajorMinor(camelVersion), 
mavenResolver, quarkusExtensionRegistryBaseUri);
+        return resolved.orElse(null);
+    }
 
-        String majorMinor = 
VersionHelper.getMajorMinorVersion(buildTimeVersion);
-        Optional<String> resolved = findStreamVersion(streams, majorMinor, 
"version");
-        return resolved.orElse(buildTimeVersion);
+    public static String resolveCamelVersionFromQuarkusCamelBom(
+            MavenGav quarkusCamelBom, Function<MavenGav, MavenArtifact> 
mavenResolver) {
+        Path file = mavenResolver.apply(quarkusCamelBom).getFile().toPath();
+        XPath xPath = XPathFactory.newInstance().newXPath();
+        String expr = managedDependencyVersionXPath("org.apache.camel", 
"camel-direct");
+        try (InputStream in = Files.newInputStream(file)) {
+            String camelVersion = (String) xPath.evaluate(expr, new 
InputSource(in), XPathConstants.STRING);
+            return camelVersion;
+        } catch (IOException e) {
+            throw new UncheckedIOException("Could not read " + file, e);
+        } catch (XPathExpressionException e) {
+            throw new RuntimeException("Could not evaluate " + expr + " on 
file " + file);
+        }
     }
 
     /**
      * Fetches the platform streams array from the Quarkus platform registry.
      *
      * @return the streams JsonArray, or null if the registry is unreachable 
or the response is invalid
      */
-    private static JsonArray fetchPlatformStreams() {
+    static JsonArray fetchPlatformStreams(
+            String quarkusExtensionRegistryBaseUri,
+            boolean download,
+            boolean fresh,
+            Path registriesDir) {
+        final String quarkusPlatformUrl = quarkusExtensionRegistryBaseUri
+                                          + "/client/platforms/all"
+                                          + 
(quarkusExtensionRegistryBaseUri.startsWith("file://") ? ".json" : "");
         try {
+            final URI uri = new URI(quarkusPlatformUrl);
+            if (uri.getScheme().equals("file")) {
+                return deserialize(Files.readString(Path.of(uri)));
+            }
+
+            if (fresh && !download) {
+                throw new IllegalStateException(
+                        "Options --fresh and --download=false contradict each 
other."
+                                                + " You may want to remove one 
of them or pass and explicit --quarkus-version if the current command supports 
it");
+            }
+
+            if (!fresh || !download) {
+                Path cacheFile = cacheFile(uri, registriesDir);
+                /* Check whether there is a cached document */
+                if (Files.isRegularFile(cacheFile)) {
+
+                    if (!download || updatedToday(cacheFile)) {
+                        /* We are in offline mode or the file was already 
updated today */
+                        return deserialize(Files.readString(cacheFile));
+                    }
+                }
+            }
+
             HttpClient hc = HttpClient.newHttpClient();
             HttpResponse<String> res = hc.send(
-                    HttpRequest.newBuilder(new URI(getQuarkusPlatformUrl()))
+                    HttpRequest.newBuilder(uri)
                             .timeout(Duration.ofSeconds(2))
                             .build(),
                     HttpResponse.BodyHandlers.ofString());
 
             if (res.statusCode() == 200) {
-                JsonObject json = (JsonObject) Jsoner.deserialize(res.body());
-                JsonArray platforms = json.getCollection("platforms");
-                if (platforms == null || platforms.isEmpty()) {
-                    return null;
-                }
-                JsonObject platform = platforms.getMap(0);
-                return platform.getCollection("streams");
+                final String jsonBody = res.body();
+
+                /* Refresh the cached file */
+                Path cacheFile = cacheFile(uri, registriesDir);
+                Files.createDirectories(cacheFile.getParent());
+                Files.writeString(cacheFile, jsonBody);
+
+                return deserialize(jsonBody);
+            } else {
+                throw new RuntimeException(
+                        "Could not get " + uri + ": " + res.statusCode() + " 
to determine the Quarkus Platform version."
+                                           + " You may want to pass 
--download=false to use the cached resource if available"
+                                           + " or pass and explicit 
--quarkus-version if the current command supports it");
             }
-        } catch (Exception e) {
-            // ignore - if the registry is not reachable within 2 seconds, 
return null
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+            throw new RuntimeException("Interrupted when fetching from " + 
quarkusPlatformUrl, e);
+        } catch (IOException e) {
+            throw new RuntimeException("Could not fetch from " + 
quarkusPlatformUrl, e);
+        } catch (URISyntaxException e) {
+            throw new RuntimeException("Unvalid URI " + quarkusPlatformUrl, e);

Review Comment:
   Solved in 164c2bd



##########
dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ExportQuarkus.java:
##########
@@ -372,27 +394,44 @@ private void createMavenPom(Path settings, Path pom, 
Set<String> deps) throws Ex
         Files.writeString(pom, context);
     }
 
-    private List<Map<String, Object>> buildQuarkusDependencyList(Set<String> 
deps, CamelCatalog catalog) {
+    private List<Map<String, Object>> buildQuarkusDependencyList(Set<String> 
deps, MavenGav quarkusCamelBom) {
         List<MavenGav> gavs = new ArrayList<>();
-        for (String dep : deps) {
-            MavenGav gav = parseMavenGav(dep);
-            String gid = gav.getGroupId();
-            String aid = gav.getArtifactId();
-            // transform to camel-quarkus extension GAV
-            if ("org.apache.camel".equals(gid)) {
-                String qaid = aid.replace("camel-", "camel-quarkus-");
-                ArtifactModel<?> am = 
catalog.modelFromMavenGAV("org.apache.camel.quarkus", qaid, null);
-                if (am != null) {
-                    // use quarkus extension
-                    gav.setGroupId(am.getGroupId());
-                    gav.setArtifactId(am.getArtifactId());
-                    gav.setVersion(null); // uses BOM so version should not be 
included
-                } else {
-                    // there is no quarkus extension so use plain camel
-                    gav.setVersion(camelVersion);
+
+        Path file = 
mavenResolver.downloader().resolveArtifact(quarkusCamelBom).getFile().toPath();
+        DocumentBuilderFactory dbf = XmlHelper.createDocumentBuilderFactory();
+
+        try (InputStream in = Files.newInputStream(file)) {
+            DocumentBuilder db = dbf.newDocumentBuilder();
+            Document dom = db.parse(in);
+            XPath xPath = XPathFactory.newDefaultInstance().newXPath();
+            for (String dep : deps) {
+                MavenGav gav = parseMavenGav(dep);
+                String gid = gav.getGroupId();
+                String aid = gav.getArtifactId();
+                // transform to camel-quarkus extension GAV
+                if ("org.apache.camel".equals(gid)) {
+                    String qaid = aid.replace("camel-", "camel-quarkus-");
+                    if (nodeExists(file, dom, xPath, 
"org.apache.camel.quarkus", qaid)) {
+                        // use quarkus extension
+                        gav.setGroupId("org.apache.camel.quarkus");
+                        gav.setArtifactId(qaid);
+                        gav.setVersion(null); // uses BOM so version should 
not be included
+                    } else if (nodeExists(file, dom, xPath, 
"org.apache.camel", aid)) {
+                        // The Camel artifact is managed in Quarkus Camel BOM
+                        gav.setVersion(null);
+                    } else {
+                        // Use plain camel with hard-coded version
+                        gav.setVersion(camelVersion);
+                    }
                 }
+                gavs.add(gav);
             }
-            gavs.add(gav);
+        } catch (IOException e) {
+            throw new UncheckedIOException("Could not read " + file, e);
+        } catch (SAXException e) {
+            throw new RuntimeException("Could parse " + file, e);

Review Comment:
   Solved in 164c2bd



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to