stataru8 commented on code in PR #349:
URL: https://github.com/apache/camel-karaf/pull/349#discussion_r1638487633


##########
tooling/camel-karaf-feature-maven-plugin/src/main/java/org/apache/camel/karaf/feature/maven/AutoDetectVersionMojo.java:
##########
@@ -0,0 +1,290 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.karaf.feature.maven;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+import org.apache.karaf.features.internal.model.Bundle;
+import org.apache.karaf.features.internal.model.Feature;
+import org.apache.maven.plugins.annotations.Component;
+import org.apache.maven.plugins.annotations.LifecyclePhase;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.eclipse.aether.RepositorySystem;
+import org.eclipse.aether.RepositorySystemSession;
+import org.eclipse.aether.artifact.Artifact;
+import org.eclipse.aether.artifact.DefaultArtifact;
+import org.eclipse.aether.collection.CollectRequest;
+import org.eclipse.aether.graph.Dependency;
+import org.eclipse.aether.repository.RemoteRepository;
+import org.eclipse.aether.resolution.ArtifactResult;
+import org.eclipse.aether.resolution.DependencyRequest;
+import org.osgi.framework.Version;
+
+@Mojo(name = "auto-detect-version", defaultPhase = 
LifecyclePhase.PROCESS_RESOURCES)
+public class AutoDetectVersionMojo extends AbstractFeaturesMojo {
+
+    private static final String AUTO_DETECT_PLACEHOLDER_PREFIX = 
"${auto-detect-version";
+    private static final Pattern AUTO_DETECT_PLACEHOLDER = 
Pattern.compile("\\$\\{auto-detect-version(:alias=([^/]+)/([^}]+))?}");
+
+    private static final Pattern MVN_BASED_PROTOCOL = 
Pattern.compile("(wrap:)?mvn:([^/]+)/([^/]+)/([^$]+|\\$\\{auto-detect-version(:[^}]+)?}[^$]*)(\\$.*)?");
+
+    @Component
+    private RepositorySystem repoSystem;
+
+    @Parameter(defaultValue = "${repositorySystemSession}", readonly = true, 
required = true)
+    private RepositorySystemSession repoSession;
+
+    @Parameter(defaultValue = "${project.remoteProjectRepositories}", readonly 
= true, required = true)
+    private List<RemoteRepository> repositories;
+
+    @Override
+    protected void processFeature(Feature feature) {
+        List<Bundle> bundlesToProcess = new ArrayList<>();
+        List<Bundle> roots = new ArrayList<>();
+        for (Bundle bundle : feature.getBundle()) {
+            if (containsPlaceholder(bundle)) {
+                bundlesToProcess.add(bundle);
+            } else {
+                roots.add(bundle);
+            }
+        }
+        if (bundlesToProcess.isEmpty()) {
+            if (getLog().isDebugEnabled()) {
+                getLog().debug("No bundles found in the feature %s with the 
auto-detect version placeholder".formatted(feature.getName()));
+            }
+            return;
+        }
+        if (roots.isEmpty()) {
+            getLog().error("No root bundles found in the feature 
%s".formatted(feature.getName()));
+            return;
+        }
+        autoDetectVersion(feature, roots, bundlesToProcess);
+    }
+
+    /**
+     * Auto-detect the version of the bundles to process according to the 
dependencies of the given root.

Review Comment:
   ```suggestion
        * Auto-detect the version of the bundles to process according to the 
dependencies of the given root Bundles.
   ```



##########
tooling/camel-karaf-feature-maven-plugin/src/main/java/org/apache/camel/karaf/feature/maven/AutoDetectVersionMojo.java:
##########
@@ -0,0 +1,290 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.karaf.feature.maven;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+import org.apache.karaf.features.internal.model.Bundle;
+import org.apache.karaf.features.internal.model.Feature;
+import org.apache.maven.plugins.annotations.Component;
+import org.apache.maven.plugins.annotations.LifecyclePhase;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.eclipse.aether.RepositorySystem;
+import org.eclipse.aether.RepositorySystemSession;
+import org.eclipse.aether.artifact.Artifact;
+import org.eclipse.aether.artifact.DefaultArtifact;
+import org.eclipse.aether.collection.CollectRequest;
+import org.eclipse.aether.graph.Dependency;
+import org.eclipse.aether.repository.RemoteRepository;
+import org.eclipse.aether.resolution.ArtifactResult;
+import org.eclipse.aether.resolution.DependencyRequest;
+import org.osgi.framework.Version;
+
+@Mojo(name = "auto-detect-version", defaultPhase = 
LifecyclePhase.PROCESS_RESOURCES)
+public class AutoDetectVersionMojo extends AbstractFeaturesMojo {
+
+    private static final String AUTO_DETECT_PLACEHOLDER_PREFIX = 
"${auto-detect-version";
+    private static final Pattern AUTO_DETECT_PLACEHOLDER = 
Pattern.compile("\\$\\{auto-detect-version(:alias=([^/]+)/([^}]+))?}");
+
+    private static final Pattern MVN_BASED_PROTOCOL = 
Pattern.compile("(wrap:)?mvn:([^/]+)/([^/]+)/([^$]+|\\$\\{auto-detect-version(:[^}]+)?}[^$]*)(\\$.*)?");
+
+    @Component
+    private RepositorySystem repoSystem;
+
+    @Parameter(defaultValue = "${repositorySystemSession}", readonly = true, 
required = true)
+    private RepositorySystemSession repoSession;
+
+    @Parameter(defaultValue = "${project.remoteProjectRepositories}", readonly 
= true, required = true)
+    private List<RemoteRepository> repositories;
+
+    @Override
+    protected void processFeature(Feature feature) {
+        List<Bundle> bundlesToProcess = new ArrayList<>();
+        List<Bundle> roots = new ArrayList<>();
+        for (Bundle bundle : feature.getBundle()) {
+            if (containsPlaceholder(bundle)) {
+                bundlesToProcess.add(bundle);
+            } else {
+                roots.add(bundle);
+            }
+        }
+        if (bundlesToProcess.isEmpty()) {
+            if (getLog().isDebugEnabled()) {
+                getLog().debug("No bundles found in the feature %s with the 
auto-detect version placeholder".formatted(feature.getName()));
+            }
+            return;
+        }
+        if (roots.isEmpty()) {
+            getLog().error("No root bundles found in the feature 
%s".formatted(feature.getName()));
+            return;
+        }
+        autoDetectVersion(feature, roots, bundlesToProcess);
+    }
+
+    /**
+     * Auto-detect the version of the bundles to process according to the 
dependencies of the given root.
+     *
+     * @param feature the feature that contains the bundles
+     * @param roots the root bundles from which the dependencies are resolved
+     * @param bundlesToProcess the bundles for which the version should be 
auto-detected
+     */
+    private void autoDetectVersion(Feature feature, List<Bundle> roots, 
List<Bundle> bundlesToProcess) {
+        Map<String, BundleVersion> dependencies = resolveDependencies(roots);
+        if (dependencies.isEmpty()) {
+            getLog().error("No dependencies found for the root bundles in the 
feature %s".formatted(feature.getName()));
+            return;
+        }
+        for (Bundle bundle : bundlesToProcess) {
+            autoDetectVersion(feature, bundle, dependencies);
+        }
+    }
+
+    /**
+     * Auto-detect the version of the given bundle according to the provided 
dependencies.
+     *
+     * @param feature the feature that contains the bundle
+     * @param bundle the bundle for which the version should be auto-detected
+     * @param dependencies the dependencies to use for the auto-detection 
where the key is the group id / artifact id
+     *                     and the value is the version
+     */
+    private void autoDetectVersion(Feature feature, Bundle bundle, Map<String, 
BundleVersion> dependencies) {
+        String location = bundle.getLocation();
+        Matcher matcher = MVN_BASED_PROTOCOL.matcher(location);
+        if (!matcher.matches()) {
+            getLog().warn("Bundle location %s does not match with a maven 
based protocol in the feature %s".formatted(location, feature.getName()));
+            return;
+        }
+        final String groupId;
+        final String artifactId;
+        Matcher aliasMatcher = 
AUTO_DETECT_PLACEHOLDER.matcher(matcher.group(4));
+        if (!aliasMatcher.find()) {
+            getLog().warn("Bundle location %s does not match with a 
placeholder syntax in the feature %s".formatted(location, feature.getName()));
+            return;
+        }
+        if (aliasMatcher.group(2) != null && aliasMatcher.group(3) != null) {
+            groupId = aliasMatcher.group(2);
+            artifactId = aliasMatcher.group(3);
+            if (getLog().isDebugEnabled()) {
+                getLog().debug("Alias %s/%s detected for the artifact %s in 
the feature %s".formatted(groupId, artifactId, location, feature.getName()));
+            }
+        } else {
+            groupId = matcher.group(2);
+            artifactId = matcher.group(3);
+        }
+        BundleVersion version = dependencies.get("%s/%s".formatted(groupId, 
artifactId));
+        if (version == null) {
+            getLog().error("Version of the artifact %s/%s could not be 
auto-detected in the feature %s".formatted(groupId, artifactId, 
feature.getName()));
+            return;
+        }
+        if (getLog().isDebugEnabled()) {
+            getLog().debug("Version %s detected for the artifact %s/%s in the 
feature %s".formatted(version, groupId, artifactId, feature.getName()));
+        }
+        
bundle.setLocation(AUTO_DETECT_PLACEHOLDER.matcher(location).replaceAll(version.toString()));
+    }
+
+    /**
+     * Resolve the dependencies of the given root bundles. In case of 
conflicts, the highest version is kept.
+     *
+     * @param roots the root bundles from which the dependencies are resolved
+     * @return the dependencies of the given root bundles where the key is the 
group id / artifact id and the value is
+     * the version
+     */
+    private Map<String, BundleVersion> resolveDependencies(List<Bundle> roots) 
{
+        Map<String, BundleVersion> dependencies = new HashMap<>();
+        for (Bundle root : roots) {
+            putAllDependencies(dependencies, resolveDependencies(root));
+        }
+        return dependencies;
+    }
+
+    /**
+     * Put all the dependencies in the given map. In case of conflicts, the 
highest version is kept.
+     *
+     * @param all the map to fill with the dependencies
+     * @param dependencies the dependencies to put in the map
+     */
+    private void putAllDependencies(Map<String, BundleVersion> all, 
Map<String, BundleVersion> dependencies) {
+        for (Map.Entry<String, BundleVersion> entry : dependencies.entrySet()) 
{
+            all.compute(entry.getKey(), (k, v) -> v == null || 
v.compareTo(entry.getValue()) < 0 ? entry.getValue() : v);
+        }
+    }
+
+    /**
+     * Resolve the dependencies of the given root bundle. In case of 
conflicts, the highest version is kept.
+     *
+     * @param root the root bundle from which the dependencies are resolved
+     * @return the dependencies of the given root bundle where the key is the 
group id / artifact id and the value is
+     * the version
+     */
+    private Map<String, BundleVersion> resolveDependencies(Bundle root) {
+        String location = root.getLocation();
+        if (location == null) {
+            getLog().warn("Root bundle location is null");
+            return Map.of();
+        }
+        Matcher matcher = MVN_BASED_PROTOCOL.matcher(location);
+        if (!matcher.matches()) {
+            getLog().warn("Root bundle location %s is not a Maven 
location".formatted(location));
+            return Map.of();
+        }
+        List<Artifact> artifacts = resolveDependencies(matcher.group(2), 
matcher.group(3), matcher.group(4));
+        if (artifacts.isEmpty()) {
+            return Map.of();
+        }
+        Map<String, BundleVersion> dependencies = new HashMap<>();
+        for (Artifact artifact : artifacts) {
+            putArtifact(dependencies, artifact);
+        }
+        return dependencies;
+    }
+
+    /**
+     * Put the artifact in the given map. In case of conflicts, the highest 
version is kept.
+     *
+     * @param dependencies the map to fill with the artifact
+     * @param artifact the artifact to put in the map
+     */
+    private static void putArtifact(Map<String, BundleVersion> dependencies, 
Artifact artifact) {
+        dependencies.compute("%s/%s".formatted(artifact.getGroupId(), 
artifact.getArtifactId()),
+            (k, v) -> {
+                BundleVersion v2 = 
BundleVersion.parseVersion(artifact.getVersion());
+                if (v == null) {
+                    return v2;
+                }
+                return v.compareTo(v2) >= 0 ? v : v2;
+        });
+    }
+
+    /**
+     * Resolve the dependencies of the given maven coordinates.
+     *
+     * @param groupId the group id of the artifact for which the dependencies 
should be resolved
+     * @param artifactId the artifact id of the artifact for which the 
dependencies should be resolved
+     * @param version the version of the artifact for which the dependencies 
should be resolved
+     * @return the dependencies of the artifact corresponding to the given 
maven coordinates
+     */
+    private List<Artifact> resolveDependencies(String groupId, String 
artifactId, String version) {
+        if (getLog().isDebugEnabled()) {
+            getLog().debug("Resolving the dependencies of the artifact 
%s/%s/%s".formatted(groupId, artifactId, version));
+        }
+        DependencyRequest req = new DependencyRequest()
+                .setCollectRequest(new CollectRequest()
+                        .setRoot(new Dependency(new DefaultArtifact(groupId, 
artifactId, "jar", version), "runtime"))
+                        .setRepositories(this.repositories));
+        try {
+            return this.repoSystem.resolveDependencies(this.repoSession, req)
+                    .getArtifactResults()
+                    .stream()
+                    .map(ArtifactResult::getArtifact)
+                    .collect(Collectors.toList());
+        } catch (Exception e) {
+            if (getLog().isDebugEnabled()) {
+                getLog().warn("Dependencies of the artifact %s could not be 
resolved".formatted(artifactId), e);

Review Comment:
   this log level should be `debug` or I am missing something?



##########
tooling/camel-karaf-feature-maven-plugin/src/main/java/org/apache/camel/karaf/feature/maven/AutoDetectVersionMojo.java:
##########
@@ -0,0 +1,290 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.karaf.feature.maven;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+import org.apache.karaf.features.internal.model.Bundle;
+import org.apache.karaf.features.internal.model.Feature;
+import org.apache.maven.plugins.annotations.Component;
+import org.apache.maven.plugins.annotations.LifecyclePhase;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.eclipse.aether.RepositorySystem;
+import org.eclipse.aether.RepositorySystemSession;
+import org.eclipse.aether.artifact.Artifact;
+import org.eclipse.aether.artifact.DefaultArtifact;
+import org.eclipse.aether.collection.CollectRequest;
+import org.eclipse.aether.graph.Dependency;
+import org.eclipse.aether.repository.RemoteRepository;
+import org.eclipse.aether.resolution.ArtifactResult;
+import org.eclipse.aether.resolution.DependencyRequest;
+import org.osgi.framework.Version;
+
+@Mojo(name = "auto-detect-version", defaultPhase = 
LifecyclePhase.PROCESS_RESOURCES)
+public class AutoDetectVersionMojo extends AbstractFeaturesMojo {
+
+    private static final String AUTO_DETECT_PLACEHOLDER_PREFIX = 
"${auto-detect-version";
+    private static final Pattern AUTO_DETECT_PLACEHOLDER = 
Pattern.compile("\\$\\{auto-detect-version(:alias=([^/]+)/([^}]+))?}");
+
+    private static final Pattern MVN_BASED_PROTOCOL = 
Pattern.compile("(wrap:)?mvn:([^/]+)/([^/]+)/([^$]+|\\$\\{auto-detect-version(:[^}]+)?}[^$]*)(\\$.*)?");
+
+    @Component
+    private RepositorySystem repoSystem;
+
+    @Parameter(defaultValue = "${repositorySystemSession}", readonly = true, 
required = true)
+    private RepositorySystemSession repoSession;
+
+    @Parameter(defaultValue = "${project.remoteProjectRepositories}", readonly 
= true, required = true)
+    private List<RemoteRepository> repositories;
+
+    @Override
+    protected void processFeature(Feature feature) {
+        List<Bundle> bundlesToProcess = new ArrayList<>();
+        List<Bundle> roots = new ArrayList<>();
+        for (Bundle bundle : feature.getBundle()) {
+            if (containsPlaceholder(bundle)) {
+                bundlesToProcess.add(bundle);
+            } else {
+                roots.add(bundle);
+            }
+        }
+        if (bundlesToProcess.isEmpty()) {
+            if (getLog().isDebugEnabled()) {
+                getLog().debug("No bundles found in the feature %s with the 
auto-detect version placeholder".formatted(feature.getName()));
+            }
+            return;
+        }
+        if (roots.isEmpty()) {
+            getLog().error("No root bundles found in the feature 
%s".formatted(feature.getName()));
+            return;
+        }
+        autoDetectVersion(feature, roots, bundlesToProcess);
+    }
+
+    /**
+     * Auto-detect the version of the bundles to process according to the 
dependencies of the given root.
+     *
+     * @param feature the feature that contains the bundles
+     * @param roots the root bundles from which the dependencies are resolved
+     * @param bundlesToProcess the bundles for which the version should be 
auto-detected
+     */
+    private void autoDetectVersion(Feature feature, List<Bundle> roots, 
List<Bundle> bundlesToProcess) {
+        Map<String, BundleVersion> dependencies = resolveDependencies(roots);
+        if (dependencies.isEmpty()) {
+            getLog().error("No dependencies found for the root bundles in the 
feature %s".formatted(feature.getName()));
+            return;
+        }
+        for (Bundle bundle : bundlesToProcess) {
+            autoDetectVersion(feature, bundle, dependencies);
+        }
+    }
+
+    /**
+     * Auto-detect the version of the given bundle according to the provided 
dependencies.
+     *
+     * @param feature the feature that contains the bundle
+     * @param bundle the bundle for which the version should be auto-detected
+     * @param dependencies the dependencies to use for the auto-detection 
where the key is the group id / artifact id
+     *                     and the value is the version
+     */
+    private void autoDetectVersion(Feature feature, Bundle bundle, Map<String, 
BundleVersion> dependencies) {
+        String location = bundle.getLocation();
+        Matcher matcher = MVN_BASED_PROTOCOL.matcher(location);
+        if (!matcher.matches()) {
+            getLog().warn("Bundle location %s does not match with a maven 
based protocol in the feature %s".formatted(location, feature.getName()));
+            return;
+        }
+        final String groupId;
+        final String artifactId;
+        Matcher aliasMatcher = 
AUTO_DETECT_PLACEHOLDER.matcher(matcher.group(4));
+        if (!aliasMatcher.find()) {
+            getLog().warn("Bundle location %s does not match with a 
placeholder syntax in the feature %s".formatted(location, feature.getName()));
+            return;
+        }
+        if (aliasMatcher.group(2) != null && aliasMatcher.group(3) != null) {
+            groupId = aliasMatcher.group(2);
+            artifactId = aliasMatcher.group(3);
+            if (getLog().isDebugEnabled()) {
+                getLog().debug("Alias %s/%s detected for the artifact %s in 
the feature %s".formatted(groupId, artifactId, location, feature.getName()));
+            }
+        } else {
+            groupId = matcher.group(2);
+            artifactId = matcher.group(3);
+        }
+        BundleVersion version = dependencies.get("%s/%s".formatted(groupId, 
artifactId));
+        if (version == null) {
+            getLog().error("Version of the artifact %s/%s could not be 
auto-detected in the feature %s".formatted(groupId, artifactId, 
feature.getName()));
+            return;
+        }
+        if (getLog().isDebugEnabled()) {
+            getLog().debug("Version %s detected for the artifact %s/%s in the 
feature %s".formatted(version, groupId, artifactId, feature.getName()));
+        }
+        
bundle.setLocation(AUTO_DETECT_PLACEHOLDER.matcher(location).replaceAll(version.toString()));
+    }
+
+    /**
+     * Resolve the dependencies of the given root bundles. In case of 
conflicts, the highest version is kept.
+     *
+     * @param roots the root bundles from which the dependencies are resolved
+     * @return the dependencies of the given root bundles where the key is the 
group id / artifact id and the value is
+     * the version
+     */
+    private Map<String, BundleVersion> resolveDependencies(List<Bundle> roots) 
{
+        Map<String, BundleVersion> dependencies = new HashMap<>();
+        for (Bundle root : roots) {
+            putAllDependencies(dependencies, resolveDependencies(root));
+        }
+        return dependencies;
+    }
+
+    /**
+     * Put all the dependencies in the given map. In case of conflicts, the 
highest version is kept.
+     *
+     * @param all the map to fill with the dependencies
+     * @param dependencies the dependencies to put in the map
+     */
+    private void putAllDependencies(Map<String, BundleVersion> all, 
Map<String, BundleVersion> dependencies) {
+        for (Map.Entry<String, BundleVersion> entry : dependencies.entrySet()) 
{
+            all.compute(entry.getKey(), (k, v) -> v == null || 
v.compareTo(entry.getValue()) < 0 ? entry.getValue() : v);
+        }
+    }
+
+    /**
+     * Resolve the dependencies of the given root bundle. In case of 
conflicts, the highest version is kept.
+     *
+     * @param root the root bundle from which the dependencies are resolved
+     * @return the dependencies of the given root bundle where the key is the 
group id / artifact id and the value is
+     * the version
+     */
+    private Map<String, BundleVersion> resolveDependencies(Bundle root) {
+        String location = root.getLocation();
+        if (location == null) {
+            getLog().warn("Root bundle location is null");
+            return Map.of();
+        }
+        Matcher matcher = MVN_BASED_PROTOCOL.matcher(location);
+        if (!matcher.matches()) {
+            getLog().warn("Root bundle location %s is not a Maven 
location".formatted(location));
+            return Map.of();
+        }
+        List<Artifact> artifacts = resolveDependencies(matcher.group(2), 
matcher.group(3), matcher.group(4));
+        if (artifacts.isEmpty()) {
+            return Map.of();
+        }
+        Map<String, BundleVersion> dependencies = new HashMap<>();
+        for (Artifact artifact : artifacts) {
+            putArtifact(dependencies, artifact);
+        }
+        return dependencies;
+    }
+
+    /**
+     * Put the artifact in the given map. In case of conflicts, the highest 
version is kept.
+     *
+     * @param dependencies the map to fill with the artifact
+     * @param artifact the artifact to put in the map
+     */
+    private static void putArtifact(Map<String, BundleVersion> dependencies, 
Artifact artifact) {
+        dependencies.compute("%s/%s".formatted(artifact.getGroupId(), 
artifact.getArtifactId()),
+            (k, v) -> {
+                BundleVersion v2 = 
BundleVersion.parseVersion(artifact.getVersion());
+                if (v == null) {
+                    return v2;
+                }
+                return v.compareTo(v2) >= 0 ? v : v2;
+        });
+    }
+
+    /**
+     * Resolve the dependencies of the given maven coordinates.
+     *
+     * @param groupId the group id of the artifact for which the dependencies 
should be resolved
+     * @param artifactId the artifact id of the artifact for which the 
dependencies should be resolved
+     * @param version the version of the artifact for which the dependencies 
should be resolved
+     * @return the dependencies of the artifact corresponding to the given 
maven coordinates
+     */
+    private List<Artifact> resolveDependencies(String groupId, String 
artifactId, String version) {
+        if (getLog().isDebugEnabled()) {
+            getLog().debug("Resolving the dependencies of the artifact 
%s/%s/%s".formatted(groupId, artifactId, version));
+        }
+        DependencyRequest req = new DependencyRequest()
+                .setCollectRequest(new CollectRequest()
+                        .setRoot(new Dependency(new DefaultArtifact(groupId, 
artifactId, "jar", version), "runtime"))
+                        .setRepositories(this.repositories));
+        try {
+            return this.repoSystem.resolveDependencies(this.repoSession, req)
+                    .getArtifactResults()
+                    .stream()
+                    .map(ArtifactResult::getArtifact)
+                    .collect(Collectors.toList());
+        } catch (Exception e) {
+            if (getLog().isDebugEnabled()) {
+                getLog().warn("Dependencies of the artifact %s could not be 
resolved".formatted(artifactId), e);
+            } else {
+                getLog().warn("Dependencies of the artifact %s could not be 
resolved: %s".formatted(artifactId, e.getMessage()));
+            }
+        }
+        return List.of();
+    }
+
+    /**
+     * Indicates whether the given bundle contains the auto-detect version 
placeholder.
+     *
+     * @param bundle the bundle to check
+     * @return {@code true} if the given bundle contains the auto-detect 
version placeholder, {@code false} otherwise
+     */
+    private static boolean containsPlaceholder(Bundle bundle) {
+        String location = bundle.getLocation();
+        return location != null && 
location.contains(AUTO_DETECT_PLACEHOLDER_PREFIX);
+    }
+
+    /**
+     * Represents a bundle version that can be compared even if the version is 
not a valid OSGi version.
+     */
+    private record BundleVersion(String originalVersion, Version version) 
implements Comparable<BundleVersion> {
+
+        static BundleVersion parseVersion(String version) {
+            try {
+                return new BundleVersion(version, 
Version.parseVersion(version));
+            } catch (IllegalArgumentException e) {
+                // The version is not a valid OSGi version

Review Comment:
   Are there any negative side effect in using 
`Version.parseVersion(VersionCleaner.clean(version))` in the case of 
`IllegalArgumentException`?



##########
tooling/camel-karaf-maven-plugin-integration-test/src/it/auto-detect-version/pom.xml:
##########
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>org.apache.camel.karaf</groupId>
+    
<artifactId>camel-karaf-feature-maven-plugin-auto-detect-version-test</artifactId>
+    <version>1.0-SNAPSHOT</version>

Review Comment:
   Is this version correct, shouldn't be `4.6.0-SNAPSHOT`?



-- 
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: commits-unsubscr...@camel.apache.org

For queries about this service, please contact Infrastructure at:
us...@infra.apache.org

Reply via email to