This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch camel-4.10.x
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/camel-4.10.x by this push:
     new 796052a7730 CAMEL-21729: camel-jbang: dependency update pom.xml that 
can automati… (#17089)
796052a7730 is described below

commit 796052a77302e9581e90590ff03b60dffe8a6bdc
Author: Claus Ibsen <claus.ib...@gmail.com>
AuthorDate: Fri Feb 7 14:41:05 2025 +0100

    CAMEL-21729: camel-jbang: dependency update pom.xml that can automati… 
(#17089)
    
    * CAMEL-21729: camel-jbang: dependency update pom.xml that can automatic 
add new Camel JARs
---
 .../ROOT/pages/camel-4x-upgrade-guide-4_10.adoc    |   5 +
 .../modules/ROOT/pages/camel-jbang.adoc            |  22 ++-
 .../dsl/jbang/core/commands/DependencyList.java    |   9 +
 .../dsl/jbang/core/commands/DependencyUpdate.java  | 206 +++++++++++++++++++--
 .../org/apache/camel/tooling/maven/MavenGav.java   |  23 +++
 5 files changed, 246 insertions(+), 19 deletions(-)

diff --git 
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_10.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_10.adoc
index 3c34504a565..7698380408f 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_10.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_10.adoc
@@ -193,6 +193,11 @@ The `camel-jgroups-cluster-service-starter` in Camel 
Spring Boot has been remove
 
 The camel-jbang commands for `camel-k` has been removed.
 
+The `camel dependency update` has removed the option `--source` to specify the 
source file,
+but to refer to the source file directly such as:
+
+`camel dependency update --source=MyRoute.java` to be `camel dependency update 
MyRoute.java`.
+
 === camel-micrometer
 
 We have fixed a flawed behavior when using dynamic endpoints which made the 
generation of endpoint events to grow in an uncontrolled way. From now on the 
component will generate events for the endpoint base URI as a default behavior. 
If you still want to collect events for the extended URI (including the 
parameters), then, you can use the 
`camel.metrics.baseEndpointURIExchangeEventNotifier=false` configuration. Mind 
that this is strongly discouraged as it can make your number of events g [...]
diff --git a/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc
index f54a3abccf3..c1db7087040 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc
@@ -2644,7 +2644,7 @@ public class foo extends EndpointRouteBuilder {
 }
 ----
 
-TIP: You can use `camel dependency update --source=foo.java` to update the 
dependencies.
+TIP: You can use `camel dependency update foo.java` to update the dependencies.
 
 Notice how we specify 
https://www.jbang.dev/documentation/guide/latest/dependencies.html[JBang 
dependencies] at the top of the file.
 We want JBang to know and prepare for the IDE of choice.
@@ -2666,7 +2666,7 @@ When working with Java source code, then you can keep the 
JBang dependencies up
 
 [source,bash]
 ----
-$ camel dependency update --source=foo.java
+$ camel dependency update foo.java
 ----
 
 TIP: You can use `--clean` to not keep any existing dependencies and generate 
a clean fresh list.
@@ -2676,6 +2676,24 @@ This will then automatic insert or update the JBang 
dependencies (`//DEPS`) in t
 You may want to use this for making it easier to load the source into an IDE 
editor to do coding.
 See the previous section for more details.
 
+===== Updating dependencies in Maven projects
+
+Camel JBang can also help with keeping Camel Maven dependencies up-to-date for 
Maven based projects.
+
+For example if start using new Camel components, in your Camel routes. Then 
you would have to
+add the corresponding Camel JAR dependencies to the pom.xml file.
+
+You can use the `camel dependency update` command to automate this, by 
executing from the project folder:
+
+[source,bash]
+----
+$ camel dependency update pom.xml
+----
+
+Notice, that only adding new Camel dependencies is supported. If you remove a 
component,
+then you need to remove the dependency from the pom.xml manually.
+Also, this is only intended for production code from `src/main` folder.
+
 ==== Camel route debugging using VSCode or IDEA editors
 
 The Camel route debugger is available by default (the `camel-debug` component 
is automatically added to the classpath). By default, it can be reached through 
JMX at the URL `service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi/camel`.
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/DependencyList.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/DependencyList.java
index 2c4565a4e4c..c2c8a3d4ae6 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/DependencyList.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/DependencyList.java
@@ -88,6 +88,15 @@ public class DependencyList extends Export {
                 String quarkusVersion = null;
                 for (int i = 0; i < nl.getLength(); i++) {
                     Element node = (Element) nl.item(i);
+
+                    // must be child at <project/dependencyManagement> or 
<project/dependencies>
+                    String p = node.getParentNode().getNodeName();
+                    String p2 = 
node.getParentNode().getParentNode().getNodeName();
+                    boolean accept = "project".equals(p2) && 
(p.equals("dependencyManagement") || p.equals("dependencies"));
+                    if (!accept) {
+                        continue;
+                    }
+
                     String g = 
node.getElementsByTagName("groupId").item(0).getTextContent();
                     String a = 
node.getElementsByTagName("artifactId").item(0).getTextContent();
                     String v = null;
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/DependencyUpdate.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/DependencyUpdate.java
index d4b8f0dbf33..9557f21abe0 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/DependencyUpdate.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/DependencyUpdate.java
@@ -17,31 +17,41 @@
 package org.apache.camel.dsl.jbang.core.commands;
 
 import java.io.File;
+import java.io.FileInputStream;
 import java.nio.file.Files;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.StringJoiner;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
 
 import org.apache.camel.tooling.maven.MavenGav;
 import org.apache.camel.util.IOHelper;
+import org.apache.camel.util.StringHelper;
+import org.apache.camel.util.xml.XmlLineNumberParser;
+import org.apache.logging.log4j.util.Strings;
 import picocli.CommandLine;
 
 @CommandLine.Command(name = "update",
-                     description = "Updates JBang style dependencies in source 
file", sortOptions = false,
+                     description = "Updates dependencies in Maven pom.xml or 
Java source file (JBang style)",
+                     sortOptions = false,
                      showDefaultValues = true)
 public class DependencyUpdate extends DependencyList {
 
-    @CommandLine.Option(names = { "--source" },
-                        description = "Camel source such as .java file to have 
dependencies updated (//DEPS)",
-                        required = true)
-    protected String source;
+    @CommandLine.Parameters(description = "Maven pom.xml or Java source files 
(JBang Style with //DEPS) to have dependencies updated",
+                            arity = "1")
+    public File file;
 
     @CommandLine.Option(names = { "--clean" },
-                        description = "Regenerate list of dependencies (do not 
keep existing dependencies)")
+                        description = "Regenerate list of dependencies (do not 
keep existing dependencies). Not supported for pom.xml")
     protected boolean clean;
 
     private final List<String> deps = new ArrayList<>();
-    private File target;
+    private final List<MavenGav> gavs = new ArrayList<>();
 
     public DependencyUpdate(CamelJBangMain main) {
         super(main);
@@ -50,15 +60,16 @@ public class DependencyUpdate extends DependencyList {
     @Override
     public Integer doCall() throws Exception {
         // source file must exist
-        target = new File(source);
-        if (!target.exists()) {
-            printer().printErr("Source file does not exist: " + target);
+        if (!file.exists()) {
+            printer().printErr("Source file does not exist: " + file);
             return -1;
         }
 
-        if (clean) {
+        boolean maven = "pom.xml".equals(file.getName());
+
+        if (clean && !maven) {
             // remove DEPS in source file first
-            updateSource();
+            updateJBangSource();
         }
 
         return super.doCall();
@@ -66,6 +77,28 @@ public class DependencyUpdate extends DependencyList {
 
     @Override
     protected void outputGav(MavenGav gav, int index, int total) {
+        try {
+            boolean maven = "pom.xml".equals(file.getName());
+            if (maven) {
+                outputGavMaven(gav, index, total);
+            } else {
+                outputGavJBang(gav, index, total);
+            }
+        } catch (Exception e) {
+            printer().printErr("Cannot update dependencies due to " + 
e.getMessage(), e);
+        }
+    }
+
+    protected void outputGavMaven(MavenGav gav, int index, int total) throws 
Exception {
+        gavs.add(gav);
+
+        boolean last = total - index <= 1;
+        if (last) {
+            updateMavenSource();
+        }
+    }
+
+    protected void outputGavJBang(MavenGav gav, int index, int total) {
         if (index == 0) {
             deps.add("//DEPS org.apache.camel:camel-bom:" + gav.getVersion() + 
"@pom");
         }
@@ -79,13 +112,13 @@ public class DependencyUpdate extends DependencyList {
         }
         boolean last = total - index <= 1;
         if (last) {
-            updateSource();
+            updateJBangSource();
         }
     }
 
-    private void updateSource() {
+    private void updateJBangSource() {
         try {
-            List<String> lines = Files.readAllLines(target.toPath());
+            List<String> lines = Files.readAllLines(file.toPath());
             List<String> answer = new ArrayList<>();
 
             // find position of where the old DEPS was
@@ -117,9 +150,148 @@ public class DependencyUpdate extends DependencyList {
             }
 
             String text = String.join(System.lineSeparator(), answer);
-            IOHelper.writeText(text, target);
+            IOHelper.writeText(text, file);
         } catch (Exception e) {
-            printer().printErr("Error updating source file: " + target + " due 
to: " + e.getMessage());
+            printer().printErr("Error updating source file: " + file + " due 
to: " + e.getMessage());
+        }
+    }
+
+    private void updateMavenSource() throws Exception {
+        List<MavenGav> existingGavs = new ArrayList<>();
+
+        Node camelClone = null;
+        int targetLineNumber = -1;
+
+        File pom = new File(file.getName());
+        if (pom.exists()) {
+            // use line number parser as we want to find where to add new 
Camel JARs after the existing Camel JARs
+            Document dom = XmlLineNumberParser.parseXml(new 
FileInputStream(pom));
+            String camelVersion = null;
+            NodeList nl = dom.getElementsByTagName("dependency");
+            for (int i = 0; i < nl.getLength(); i++) {
+                Element node = (Element) nl.item(i);
+
+                // must be child at <project/dependencyManagement> or 
<project/dependencies>
+                String p = node.getParentNode().getNodeName();
+                String p2 = node.getParentNode().getParentNode().getNodeName();
+                boolean accept = "project".equals(p2) && 
(p.equals("dependencyManagement") || p.equals("dependencies"));
+                if (!accept) {
+                    continue;
+                }
+
+                String g = 
node.getElementsByTagName("groupId").item(0).getTextContent();
+                String a = 
node.getElementsByTagName("artifactId").item(0).getTextContent();
+                String v = null;
+                NodeList vl = node.getElementsByTagName("version");
+                if (vl.getLength() > 0) {
+                    v = vl.item(0).getTextContent();
+                }
+                String scope = null;
+                vl = node.getElementsByTagName("scope");
+                if (vl.getLength() > 0) {
+                    scope = vl.item(0).getTextContent();
+                }
+                if (scope != null && !"compile".equals(scope)) {
+                    continue;
+                }
+
+                if ("org.apache.camel".equals(g)) {
+                    camelVersion = v;
+                    if (camelClone == null && !"camel-bom".equals(a)) {
+                        camelClone = node.cloneNode(true);
+                    }
+                    String num = (String) 
node.getUserData(XmlLineNumberParser.LINE_NUMBER_END);
+                    if (num != null) {
+                        targetLineNumber = Integer.parseInt(num);
+                    }
+                    num = (String) 
node.getUserData(XmlLineNumberParser.LINE_NUMBER_END);
+                    if (num != null) {
+                        targetLineNumber = Integer.parseInt(num);
+                    }
+                }
+                if ("org.apache.camel.springboot".equals(g)) {
+                    camelVersion = v;
+                    String num = (String) 
node.getUserData(XmlLineNumberParser.LINE_NUMBER_END);
+                    if (num != null) {
+                        targetLineNumber = Integer.parseInt(num);
+                    }
+                }
+                if ("org.apache.camel.quarkus".equals(g)) {
+                    String num = (String) 
node.getUserData(XmlLineNumberParser.LINE_NUMBER_END);
+                    if (num != null) {
+                        targetLineNumber = Integer.parseInt(num);
+                    }
+                }
+                if (v != null) {
+                    existingGavs.add(MavenGav.parseGav(g + ":" + a + ":" + v));
+                } else {
+                    existingGavs.add(MavenGav.parseGav(g + ":" + a));
+                }
+            }
+
+            // find out which JARs are new
+            List<MavenGav> updates = new ArrayList<>();
+            for (MavenGav gav : gavs) {
+                MavenGav target;
+                if (camelVersion != null) {
+                    target = MavenGav.parseGav(gav.getGroupId() + ":" + 
gav.getArtifactId() + ":" + camelVersion);
+                } else {
+                    target = MavenGav.parseGav(gav.getGroupId() + ":" + 
gav.getArtifactId());
+                }
+                updates.add(target);
+            }
+            // sort the new JARs being added
+            updates.sort(mavenGavComparator());
+            List<MavenGav> toBeUpdated = new ArrayList<>();
+            int changes = 0;
+            for (MavenGav update : updates) {
+                if (!existingGavs.contains(update)) {
+                    toBeUpdated.add(update);
+                    changes++;
+                }
+            }
+
+            if (changes > 0) {
+                // respect indent from existing GAVs
+                String line = IOHelper.loadTextLine(new FileInputStream(file), 
targetLineNumber);
+                line = StringHelper.before(line, "<");
+                int indent = StringHelper.countChar(line, ' ');
+                String pad = Strings.repeat(" ", indent);
+                line = IOHelper.loadTextLine(new FileInputStream(file), 
targetLineNumber - 1);
+                line = StringHelper.before(line, "<");
+                int indent2 = StringHelper.countChar(line, ' ');
+                String pad2 = Strings.repeat(" ", indent2);
+
+                // build GAVs to be added to pom.xml
+                StringJoiner sj = new StringJoiner("");
+                for (MavenGav gav : toBeUpdated) {
+                    sj.add(pad).add("<dependency>\n");
+                    sj.add(pad2).add("<groupId>" + gav.getGroupId() + 
"</groupId>\n");
+                    sj.add(pad2).add("<artifactId>" + gav.getArtifactId() + 
"</artifactId>\n");
+                    if (gav.getVersion() != null) {
+                        sj.add(pad2).add("<version>" + gav.getVersion() + 
"</version>\n");
+                    }
+                    sj.add(pad).add("</dependency>");
+                }
+
+                StringJoiner out = new StringJoiner("\n");
+                String[] lines = IOHelper.loadText(new 
FileInputStream(file)).split("\n");
+                for (int i = 0; i < lines.length; i++) {
+                    String txt = lines[i];
+                    out.add(txt);
+                    if (i == targetLineNumber - 1) {
+                        out.add(sj.toString());
+                    }
+                }
+                if (changes > 1) {
+                    outPrinter().println("Updating pom.xml with " + changes + 
" dependencies added");
+                } else {
+                    outPrinter().println("Updating pom.xml with 1 dependency 
added");
+                }
+                IOHelper.writeText(out.toString(), file);
+            } else {
+                outPrinter().println("No updates to pom.xml");
+            }
         }
     }
 
diff --git 
a/tooling/camel-tooling-maven/src/main/java/org/apache/camel/tooling/maven/MavenGav.java
 
b/tooling/camel-tooling-maven/src/main/java/org/apache/camel/tooling/maven/MavenGav.java
index 2237c0abeca..dcfb5c515d4 100644
--- 
a/tooling/camel-tooling-maven/src/main/java/org/apache/camel/tooling/maven/MavenGav.java
+++ 
b/tooling/camel-tooling-maven/src/main/java/org/apache/camel/tooling/maven/MavenGav.java
@@ -16,6 +16,8 @@
  */
 package org.apache.camel.tooling.maven;
 
+import java.util.Objects;
+
 /**
  * Maven GAV model with parsing support and special rules for some names:
  * <ul>
@@ -171,6 +173,27 @@ public final class MavenGav {
         this.classifier = classifier;
     }
 
+    @Override
+    public boolean equals(Object o) {
+        if (!(o instanceof MavenGav mavenGav)) {
+            return false;
+        }
+
+        return groupId.equals(mavenGav.groupId) && 
artifactId.equals(mavenGav.artifactId)
+                && Objects.equals(version, mavenGav.version) && 
Objects.equals(packaging, mavenGav.packaging)
+                && Objects.equals(classifier, mavenGav.classifier);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = groupId.hashCode();
+        result = 31 * result + artifactId.hashCode();
+        result = 31 * result + Objects.hashCode(version);
+        result = 31 * result + Objects.hashCode(packaging);
+        result = 31 * result + Objects.hashCode(classifier);
+        return result;
+    }
+
     @Override
     public String toString() {
         if (version != null) {

Reply via email to