You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by nf...@apache.org on 2022/06/24 17:07:37 UTC

[camel] branch CAMEL-18223/debug-goal-for-camel-maven-plugin updated (3ed3ee0591e -> 3040f2e56ee)

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

nfilotto pushed a change to branch CAMEL-18223/debug-goal-for-camel-maven-plugin
in repository https://gitbox.apache.org/repos/asf/camel.git


 discard 3ed3ee0591e CAMEL-18223: camel-plugin - Propose a debug goal to enable the textual route debugger
     new 3040f2e56ee CAMEL-18223: camel-plugin - Propose a goal to debug with the textual route debugger

This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version.  This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:

 * -- * -- B -- O -- O -- O   (3ed3ee0591e)
            \
             N -- N -- N   refs/heads/CAMEL-18223/debug-goal-for-camel-maven-plugin (3040f2e56ee)

You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.

Any revisions marked "omit" are not gone; other references still
refer to them.  Any revisions marked "discard" are gone forever.

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:


[camel] 01/01: CAMEL-18223: camel-plugin - Propose a goal to debug with the textual route debugger

Posted by nf...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

nfilotto pushed a commit to branch CAMEL-18223/debug-goal-for-camel-maven-plugin
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 3040f2e56eea7578864f51e3b1418669f8439c84
Author: Nicolas Filotto <nf...@talend.com>
AuthorDate: Fri Jun 24 18:53:28 2022 +0200

    CAMEL-18223: camel-plugin - Propose a goal to debug with the textual route debugger
---
 .../src/main/docs/camel-maven-plugin.adoc          |  13 ++
 .../java/org/apache/camel/maven/DebugMojo.java     | 150 ++++++++++++++++
 .../main/java/org/apache/camel/maven/RunMojo.java  | 196 +++++++++++----------
 3 files changed, 265 insertions(+), 94 deletions(-)

diff --git a/tooling/maven/camel-maven-plugin/src/main/docs/camel-maven-plugin.adoc b/tooling/maven/camel-maven-plugin/src/main/docs/camel-maven-plugin.adoc
index fb8b685806a..92a3efead96 100644
--- a/tooling/maven/camel-maven-plugin/src/main/docs/camel-maven-plugin.adoc
+++ b/tooling/maven/camel-maven-plugin/src/main/docs/camel-maven-plugin.adoc
@@ -114,6 +114,19 @@ The maven plugin *dev* goal supports the following options which can be configur
 | loggingLevel | OFF | Whether to use built-in console logging (uses log4j), which does not require to add any logging dependency to your project. However, the logging is fixed to log to the console, with a color style that is similar to Spring Boot. You can change the root logging level to: FATAL, ERROR, WARN, INFO, DEBUG, TRACE, OFF
 |===
 
+== camel:debug
+
+The `camel:debug` is an extension to `camel:dev` to run the Camel application in debug mode which allows to have set up automatically to be able to debug the camel routes using the camel textual route debugger.
+
+=== Options
+
+The maven plugin *debug* goal supports the following options which can be configured from the command line (use `-D` syntax), or defined in the `pom.xml` file in the `<configuration>` tag.
+
+|===
+| Parameter | Default Value | Description
+| suspend | true | Indicates whether the message processing done by Camel should be suspended as long as a debugger is not attached.
+|===
+
 == camel:prepare-fatjar
 
 The `camel:prepare-fatjar` goal of the Camel Maven Plugin is used to prepare your Camel application
diff --git a/tooling/maven/camel-maven-plugin/src/main/java/org/apache/camel/maven/DebugMojo.java b/tooling/maven/camel-maven-plugin/src/main/java/org/apache/camel/maven/DebugMojo.java
new file mode 100644
index 00000000000..0fd83d3311e
--- /dev/null
+++ b/tooling/maven/camel-maven-plugin/src/main/java/org/apache/camel/maven/DebugMojo.java
@@ -0,0 +1,150 @@
+/*
+ * 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.maven;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import org.apache.camel.impl.debugger.BacklogDebugger;
+import org.apache.camel.util.CastUtils;
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.artifact.DefaultArtifact;
+import org.apache.maven.artifact.handler.DefaultArtifactHandler;
+import org.apache.maven.artifact.resolver.ArtifactResolutionRequest;
+import org.apache.maven.artifact.resolver.ArtifactResolutionResult;
+import org.apache.maven.artifact.resolver.filter.ScopeArtifactFilter;
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.plugin.MojoExecution;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.MojoFailureException;
+import org.apache.maven.plugins.annotations.LifecyclePhase;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.apache.maven.plugins.annotations.ResolutionScope;
+
+/**
+ * The maven goal allowing to automatically configure the Camel application to be able to use the Camel textual Route
+ * Debugger.
+ */
+@Mojo(name = "debug", defaultPhase = LifecyclePhase.PREPARE_PACKAGE,
+      requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME)
+public class DebugMojo extends DevMojo {
+
+    /**
+     * Indicates whether the message processing done by Camel should be suspended as long as a debugger is not attached.
+     */
+    @Parameter(property = "camel.suspend", defaultValue = "true")
+    private boolean suspend;
+
+    @Parameter(defaultValue = "${mojoExecution}", readonly = true)
+    private MojoExecution mojo;
+
+    @Parameter(defaultValue = "${session}", readonly = true)
+    private MavenSession session;
+
+    @Override
+    protected void beforeBootstrapCamel() throws Exception {
+        super.beforeBootstrapCamel();
+
+        // Enable JMX
+        System.setProperty("org.apache.camel.jmx.disabled", "false");
+        // Enable the suspend mode.
+        System.setProperty(BacklogDebugger.SUSPEND_MODE_SYSTEM_PROP_NAME, Boolean.toString(suspend));
+        String suspendMode = System.getenv(BacklogDebugger.SUSPEND_MODE_ENV_VAR_NAME);
+        if (suspendMode != null && Boolean.parseBoolean(suspendMode) != suspend) {
+            throw new MojoExecutionException(
+                    String.format(
+                            "The environment variable %s has been set and prevents to configure the suspend mode. Please remove it first.",
+                            BacklogDebugger.SUSPEND_MODE_ENV_VAR_NAME));
+        }
+    }
+
+    @Override
+    protected String goal() {
+        return "camel:debug";
+    }
+
+    @Override
+    protected List<Artifact> getClasspath() throws MojoExecutionException, MojoFailureException {
+        List<Artifact> classpath = super.getClasspath();
+        if (classpath.stream().anyMatch(artifact -> "org.apache.camel".equals(artifact.getGroupId())
+                && "camel-debug".equals(artifact.getArtifactId()))) {
+            getLog().debug("The component camel-debug has been detected in the classpath so no need to add it");
+            return classpath;
+        }
+        getLog().info("The component camel-debug is not available in the classpath, it will be added automatically");
+        Optional<String> camelCoreVersion = classpath.stream()
+                .filter(artifact -> "org.apache.camel".equals(artifact.getGroupId())
+                        && Objects.nonNull(artifact.getArtifactId()) && artifact.getArtifactId().startsWith("camel-core"))
+                .map(Artifact::getBaseVersion)
+                .filter(Objects::nonNull)
+                .findAny();
+        if (camelCoreVersion.isEmpty()) {
+            getLog().info("The version of Camel could not be detected, the version of the plugin will be used instead");
+            addCamelDebug(classpath, mojo.getVersion());
+            return classpath;
+        }
+        addCamelDebug(classpath, camelCoreVersion.get());
+        return classpath;
+    }
+
+    /**
+     * Automatically retrieve the given version of camel-debug and add it to the classpath if it can be found.
+     *
+     * @param classpath the classpath to which camel-debug and its dependencies are added.
+     * @param version   the version of camel-debug to retrieve.
+     */
+    private void addCamelDebug(List<Artifact> classpath, String version) {
+        getLog().debug(String.format("Trying to retrieve the version %s of camel-debug", version));
+        ArtifactResolutionRequest request = new ArtifactResolutionRequest();
+        request.setResolveRoot(true);
+        request.setResolveTransitively(true);
+        request.setLocalRepository(session.getLocalRepository());
+        request.setRemoteRepositories(session.getCurrentProject().getRemoteArtifactRepositories());
+        request.setOffline(session.isOffline());
+        request.setForceUpdate(session.getRequest().isUpdateSnapshots());
+        request.setServers(session.getRequest().getServers());
+        request.setMirrors(session.getRequest().getMirrors());
+        request.setProxies(session.getRequest().getProxies());
+        request.setManagedVersionMap(Collections.emptyMap());
+        request.setArtifact(
+                new DefaultArtifact(
+                        "org.apache.camel", "camel-debug", version, Artifact.SCOPE_RUNTIME, "jar", null,
+                        new DefaultArtifactHandler("jar")));
+        request.setResolutionFilter(new ScopeArtifactFilter(Artifact.SCOPE_RUNTIME));
+        ArtifactResolutionResult result = artifactResolver.resolve(request);
+        if (result.isSuccess()) {
+            getLog().info(String.format("Adding the version %s of camel-debug", version));
+            classpath.addAll(CastUtils.cast(result.getArtifacts()));
+            return;
+        }
+
+        if (result.hasMissingArtifacts()) {
+            getLog().warn(
+                    String.format(
+                            "Could not find the artifacts: %s",
+                            result.getMissingArtifacts().stream().map(Objects::toString).collect(Collectors.joining(", "))));
+        }
+        if (result.hasExceptions()) {
+            result.getExceptions().forEach(
+                    ex -> getLog().warn(String.format("An error occurred while retrieving camel-debug: %s", ex.getMessage())));
+        }
+    }
+}
diff --git a/tooling/maven/camel-maven-plugin/src/main/java/org/apache/camel/maven/RunMojo.java b/tooling/maven/camel-maven-plugin/src/main/java/org/apache/camel/maven/RunMojo.java
index fb1cfe51741..48659fa630e 100644
--- a/tooling/maven/camel-maven-plugin/src/main/java/org/apache/camel/maven/RunMojo.java
+++ b/tooling/maven/camel-maven-plugin/src/main/java/org/apache/camel/maven/RunMojo.java
@@ -57,6 +57,7 @@ import org.apache.maven.plugins.annotations.ResolutionScope;
 import org.apache.maven.project.MavenProject;
 import org.apache.maven.project.MavenProjectBuilder;
 import org.apache.maven.project.artifact.MavenMetadataSource;
+import org.apache.maven.project.artifact.ProjectArtifact;
 import org.codehaus.mojo.exec.AbstractExecMojo;
 import org.codehaus.mojo.exec.ExecutableDependency;
 import org.codehaus.mojo.exec.Property;
@@ -145,7 +146,7 @@ public class RunMojo extends AbstractExecMojo {
     protected String extendedPluginDependencyArtifactId;
 
     @Component
-    private ArtifactResolver artifactResolver;
+    protected ArtifactResolver artifactResolver;
 
     @Component
     private ArtifactFactory artifactFactory;
@@ -668,18 +669,44 @@ public class RunMojo extends AbstractExecMojo {
      * @throws MojoExecutionException
      */
     private ClassLoader getClassLoader() throws MojoExecutionException, MojoFailureException {
-        List<URL> classpathURLs = new ArrayList<>();
+        final List<Artifact> classpath = getClasspath();
+        final List<URL> classpathURLs = new ArrayList<>(classpath.size());
+        try {
+            for (Artifact artifact : classpath) {
+                File file = artifact.getFile();
+                if (file != null) {
+                    classpathURLs.add(file.toURI().toURL());
+                }
+            }
+        } catch (MalformedURLException e) {
+            throw new MojoExecutionException("Error during setting up classpath", e);
+        }
+
+        if (logClasspath) {
+            getLog().info("Classpath:");
+            for (URL url : classpathURLs) {
+                getLog().info("  " + url.getFile());
+            }
+        }
+        return new URLClassLoader(classpathURLs.toArray(new URL[0]));
+    }
+
+    /**
+     * @return the list of artifacts corresponding to the classpath to use when launching the application
+     */
+    protected List<Artifact> getClasspath() throws MojoExecutionException, MojoFailureException {
+        final List<Artifact> classpath = new ArrayList<>();
         // project classpath must be first
-        this.addRelevantProjectDependenciesToClasspath(classpathURLs);
+        this.addRelevantProjectDependenciesToClasspath(classpath);
         // and extra plugin classpath
-        this.addExtraPluginDependenciesToClasspath(classpathURLs);
+        this.addExtraPluginDependenciesToClasspath(classpath);
         // and plugin classpath last
-        this.addRelevantPluginDependenciesToClasspath(classpathURLs);
+        this.addRelevantPluginDependenciesToClasspath(classpath);
 
         if (!loggingLevel.equals("OFF")) {
             getLog().info("Using built-in logging level: " + loggingLevel);
             // and extra plugin classpath
-            this.addConsoleLogDependenciesToClasspath(classpathURLs);
+            this.addConsoleLogDependenciesToClasspath(classpath);
             // setup logging which can only be done by copying log4j.properties to project output to be in classpath
             try {
                 String out = LOG4J_TEMPLATE.replace("@@@LOGGING_LEVEL@@@", loggingLevel);
@@ -688,46 +715,36 @@ public class RunMojo extends AbstractExecMojo {
                 throw new MojoFailureException("Error configuring loggingLevel", e);
             }
         }
-
-        if (logClasspath) {
-            getLog().info("Classpath:");
-            for (URL url : classpathURLs) {
-                getLog().info("  " + url.getFile());
-            }
-        }
-        return new URLClassLoader(classpathURLs.toArray(new URL[classpathURLs.size()]));
+        return classpath;
     }
 
     /**
      * Add any relevant project dependencies to the classpath. Indirectly takes includePluginDependencies and
      * ExecutableDependency into consideration.
      *
-     * @param  path                   classpath of {@link java.net.URL} objects
+     * @param  classpath              the list of artifacts representing the classpath to which artifacts should be
+     *                                added
      * @throws MojoExecutionException
      */
-    private void addRelevantPluginDependenciesToClasspath(List<URL> path) throws MojoExecutionException {
+    private void addRelevantPluginDependenciesToClasspath(List<Artifact> classpath) throws MojoExecutionException {
         if (hasCommandlineArgs()) {
             arguments = parseCommandlineArgs();
         }
 
-        try {
-            for (Artifact classPathElement : this.determineRelevantPluginDependencies()) {
-                // we must skip org.osgi.core, otherwise we get a
-                // java.lang.NoClassDefFoundError: org.osgi.vendor.framework property not set
-                if (classPathElement.getArtifactId().equals("org.osgi.core")) {
-                    if (getLog().isDebugEnabled()) {
-                        getLog().debug("Skipping org.osgi.core -> " + classPathElement.getGroupId() + "/"
-                                       + classPathElement.getArtifactId() + "/" + classPathElement.getVersion());
-                    }
-                    continue;
+        for (Artifact classPathElement : this.determineRelevantPluginDependencies()) {
+            // we must skip org.osgi.core, otherwise we get a
+            // java.lang.NoClassDefFoundError: org.osgi.vendor.framework property not set
+            if (classPathElement.getArtifactId().equals("org.osgi.core")) {
+                if (getLog().isDebugEnabled()) {
+                    getLog().debug("Skipping org.osgi.core -> " + classPathElement.getGroupId() + "/"
+                                   + classPathElement.getArtifactId() + "/" + classPathElement.getVersion());
                 }
-
-                getLog().debug("Adding plugin dependency artifact: " + classPathElement.getArtifactId()
-                               + " to classpath");
-                path.add(classPathElement.getFile().toURI().toURL());
+                continue;
             }
-        } catch (MalformedURLException e) {
-            throw new MojoExecutionException("Error during setting up classpath", e);
+
+            getLog().debug("Adding plugin dependency artifact: " + classPathElement.getArtifactId()
+                           + " to classpath");
+            classpath.add(classPathElement);
         }
 
     }
@@ -736,100 +753,91 @@ public class RunMojo extends AbstractExecMojo {
      * Add any relevant project dependencies to the classpath. Indirectly takes includePluginDependencies and
      * ExecutableDependency into consideration.
      *
-     * @param  path                   classpath of {@link java.net.URL} objects
+     * @param  classpath              the list of artifacts representing the classpath to which artifacts should be
+     *                                added
      * @throws MojoExecutionException
      */
-    private void addExtraPluginDependenciesToClasspath(List<URL> path) throws MojoExecutionException {
+    private void addExtraPluginDependenciesToClasspath(List<Artifact> classpath) throws MojoExecutionException {
         if (extraPluginDependencyArtifactId == null && extendedPluginDependencyArtifactId == null) {
             return;
         }
 
-        try {
-            Set<Artifact> artifacts = new HashSet<>(this.pluginDependencies);
-            for (Artifact artifact : artifacts) {
-                if (artifact.getArtifactId().equals(extraPluginDependencyArtifactId)
-                        || artifact.getArtifactId().equals(extendedPluginDependencyArtifactId)) {
-                    getLog().debug("Adding extra plugin dependency artifact: " + artifact.getArtifactId()
-                                   + " to classpath");
-                    path.add(artifact.getFile().toURI().toURL());
-
-                    // add the transient dependencies of this artifact
-                    Set<Artifact> deps = resolveExecutableDependencies(artifact, true);
-                    if (deps != null) {
-                        for (Artifact dep : deps) {
-                            getLog().debug("Adding extra plugin dependency artifact: " + dep.getArtifactId()
-                                           + " to classpath");
-                            path.add(dep.getFile().toURI().toURL());
-                        }
+        final Set<Artifact> artifacts = new HashSet<>(this.pluginDependencies);
+        for (Artifact artifact : artifacts) {
+            if (artifact.getArtifactId().equals(extraPluginDependencyArtifactId)
+                    || artifact.getArtifactId().equals(extendedPluginDependencyArtifactId)) {
+                getLog().debug("Adding extra plugin dependency artifact: " + artifact.getArtifactId()
+                               + " to classpath");
+                classpath.add(artifact);
+
+                // add the transient dependencies of this artifact
+                Set<Artifact> deps = resolveExecutableDependencies(artifact, true);
+                if (deps != null) {
+                    for (Artifact dep : deps) {
+                        getLog().debug("Adding extra plugin dependency artifact: " + dep.getArtifactId()
+                                       + " to classpath");
+                        classpath.add(dep);
                     }
                 }
             }
-        } catch (MalformedURLException e) {
-            throw new MojoExecutionException("Error during setting up classpath", e);
         }
     }
 
     /**
      * Adds the JARs needed for using the built-in logging to console
      */
-    private void addConsoleLogDependenciesToClasspath(List<URL> path) throws MojoExecutionException {
-        try {
-            Set<Artifact> artifacts = new HashSet<>(this.pluginDependencies);
-            for (Artifact artifact : artifacts) {
-                // add these loggers in the beginning so they are first
-                if (artifact.getArtifactId().equals("jansi")) {
-                    // jansi for logging in color
-                    path.add(0, artifact.getFile().toURI().toURL());
-                } else if (artifact.getGroupId().equals("org.apache.logging.log4j")) {
-                    // add log4j as this is needed
-                    path.add(0, artifact.getFile().toURI().toURL());
-                } else if (artifact.getArtifactId().equals("camel-maven-plugin")) {
-                    // add ourselves
-                    path.add(0, artifact.getFile().toURI().toURL());
-                }
+    private void addConsoleLogDependenciesToClasspath(List<Artifact> classpath) {
+        Set<Artifact> artifacts = new HashSet<>(this.pluginDependencies);
+        for (Artifact artifact : artifacts) {
+            // add these loggers in the beginning so they are first
+            if (artifact.getArtifactId().equals("jansi")) {
+                // jansi for logging in color
+                classpath.add(0, artifact);
+            } else if (artifact.getGroupId().equals("org.apache.logging.log4j")) {
+                // add log4j as this is needed
+                classpath.add(0, artifact);
+            } else if (artifact.getArtifactId().equals("camel-maven-plugin")) {
+                // add ourselves
+                classpath.add(0, artifact);
             }
-        } catch (MalformedURLException e) {
-            throw new MojoExecutionException("Error during setting up classpath", e);
         }
     }
 
     /**
      * Add any relevant project dependencies to the classpath. Takes includeProjectDependencies into consideration.
      *
-     * @param  path                   classpath of {@link java.net.URL} objects
+     * @param  classpath              the list of artifacts representing the classpath to which artifacts should be
+     *                                added
      * @throws MojoExecutionException
      */
-    private void addRelevantProjectDependenciesToClasspath(List<URL> path) throws MojoExecutionException {
+    private void addRelevantProjectDependenciesToClasspath(List<Artifact> classpath) throws MojoExecutionException {
         if (this.includeProjectDependencies) {
-            try {
-                getLog().debug("Project Dependencies will be included.");
-
-                URL mainClasses = new File(project.getBuild().getOutputDirectory()).toURI().toURL();
-                getLog().debug("Adding to classpath : " + mainClasses);
-                path.add(mainClasses);
-
-                Set<Artifact> dependencies = CastUtils.cast(project.getArtifacts());
+            getLog().debug("Project Dependencies will be included.");
+
+            File mainClasses = new File(project.getBuild().getOutputDirectory());
+            getLog().debug("Adding to classpath : " + mainClasses);
+            classpath.add(
+                    new ProjectArtifact(project) {
+                        @Override
+                        public File getFile() {
+                            return mainClasses;
+                        }
+                    });
 
-                // system scope dependencies are not returned by maven 2.0. See
-                // MEXEC-17
-                dependencies.addAll(getAllNonTestScopedDependencies());
+            Set<Artifact> dependencies = CastUtils.cast(project.getArtifacts());
 
-                for (Artifact classPathElement : dependencies) {
-                    getLog().debug("Adding project dependency artifact: " + classPathElement.getArtifactId()
-                                   + " to classpath");
-                    File file = classPathElement.getFile();
-                    if (file != null) {
-                        path.add(file.toURI().toURL());
-                    }
-                }
+            // system scope dependencies are not returned by maven 2.0. See
+            // MEXEC-17
+            dependencies.addAll(getAllNonTestScopedDependencies());
 
-            } catch (MalformedURLException e) {
-                throw new MojoExecutionException("Error during setting up classpath", e);
+            for (Artifact classPathElement : dependencies) {
+                getLog().debug("Adding project dependency artifact: " + classPathElement.getArtifactId()
+                               + " to classpath");
+                classpath.add(classPathElement);
             }
         } else {
             getLog().debug("Project Dependencies will be excluded.");
         }
-
     }
 
     private Collection<Artifact> getAllNonTestScopedDependencies() throws MojoExecutionException {