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:05:18 UTC
[camel] 01/01: CAMEL-18223: camel-plugin - Propose a debug goal to enable the textual route debugger
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 3ed3ee0591e87a1d1c72281c3f6cb912736bdd05
Author: Nicolas Filotto <nf...@talend.com>
AuthorDate: Fri Jun 24 18:53:28 2022 +0200
CAMEL-18223: camel-plugin - Propose a debug goal to enable 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 {