This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch debug-jmx in repository https://gitbox.apache.org/repos/asf/camel.git
commit 8ee88834f9a3bb1ba5488c6786f275a04fa62306 Author: Claus Ibsen <claus.ib...@gmail.com> AuthorDate: Tue May 14 09:57:08 2024 +0200 CAMEL-20760: camel-core - Allow to turn on|off RMI connector for tooling based debuggers --- .../component/debug/CamelDebuggerFactory.java | 9 ++-- .../impl/debugger/DebuggerJmxConnectorService.java | 53 +++++++++------------- .../DebuggerConfigurationPropertiesConfigurer.java | 12 +++++ .../camel-main-configuration-metadata.json | 2 + core/camel-main/src/main/docs/main.adoc | 4 +- .../org/apache/camel/main/BaseMainSupport.java | 11 ++++- .../main/DebuggerConfigurationProperties.java | 44 ++++++++++++++++++ 7 files changed, 98 insertions(+), 37 deletions(-) diff --git a/components/camel-debug/src/main/java/org/apache/camel/component/debug/CamelDebuggerFactory.java b/components/camel-debug/src/main/java/org/apache/camel/component/debug/CamelDebuggerFactory.java index aaf032cc051..fb85af6fb1b 100644 --- a/components/camel-debug/src/main/java/org/apache/camel/component/debug/CamelDebuggerFactory.java +++ b/components/camel-debug/src/main/java/org/apache/camel/component/debug/CamelDebuggerFactory.java @@ -17,6 +17,7 @@ package org.apache.camel.component.debug; import org.apache.camel.CamelContext; +import org.apache.camel.impl.debugger.DebuggerJmxConnectorService; import org.apache.camel.impl.debugger.DefaultBacklogDebugger; import org.apache.camel.spi.BacklogDebugger; import org.apache.camel.spi.Debugger; @@ -43,6 +44,11 @@ public class CamelDebuggerFactory implements DebuggerFactory { // enable debugger on camel camelContext.setDebugging(true); + // to make debugging possible for tooling we need to make it possible to do remote JMX connection + DebuggerJmxConnectorService connector = new DebuggerJmxConnectorService(); + connector.setCreateConnector(true); + camelContext.addService(connector); + // we need to enable debugger after context is started camelContext.addLifecycleStrategy(new LifecycleStrategySupport() { @Override @@ -61,9 +67,6 @@ public class CamelDebuggerFactory implements DebuggerFactory { camelContext.addService(backlog, true, true); } - // to make debugging possible for tooling we need to make it possible to do remote JMX connection - camelContext.addService(new JmxConnectorService()); - // return null as we fool camel-core into using this backlog debugger as we added it as a service return null; } diff --git a/components/camel-debug/src/main/java/org/apache/camel/component/debug/JmxConnectorService.java b/core/camel-base-engine/src/main/java/org/apache/camel/impl/debugger/DebuggerJmxConnectorService.java similarity index 66% rename from components/camel-debug/src/main/java/org/apache/camel/component/debug/JmxConnectorService.java rename to core/camel-base-engine/src/main/java/org/apache/camel/impl/debugger/DebuggerJmxConnectorService.java index d0c75f85561..eab2ef53d06 100644 --- a/components/camel-debug/src/main/java/org/apache/camel/component/debug/JmxConnectorService.java +++ b/core/camel-base-engine/src/main/java/org/apache/camel/impl/debugger/DebuggerJmxConnectorService.java @@ -1,20 +1,4 @@ -/* - * 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.component.debug; +package org.apache.camel.impl.debugger; import java.io.IOException; import java.lang.management.ManagementFactory; @@ -36,22 +20,23 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * To make it possible to do JMX debugging via JMX remote + * To make it possible to do Camel debugging via JMX remote */ -public class JmxConnectorService extends ServiceSupport implements CamelContextAware { +public class DebuggerJmxConnectorService extends ServiceSupport implements CamelContextAware { public static final String DEFAULT_HOST = "localhost"; public static final int DEFAULT_REGISTRY_PORT = 1099; public static final int DEFAULT_CONNECTION_PORT = -1; public static final String DEFAULT_SERVICE_URL_PATH = "/jmxrmi/camel"; - private static final Logger LOG = LoggerFactory.getLogger(JmxConnectorService.class); + private static final Logger LOG = LoggerFactory.getLogger(DebuggerJmxConnectorService.class); private CamelContext camelContext; private MBeanServer server; private JMXConnectorServer cs; private Registry registry; + private int registryPort = DEFAULT_REGISTRY_PORT; private boolean createConnector = true; @Override @@ -72,11 +57,15 @@ public class JmxConnectorService extends ServiceSupport implements CamelContextA this.createConnector = createConnector; } + public void setRegistryPort(int registryPort) { + this.registryPort = registryPort; + } + @Override protected void doStart() throws Exception { server = ManagementFactory.getPlatformMBeanServer(); - if (createConnector) { + if (createConnector && registryPort > 0) { createJmxConnector(DEFAULT_HOST); } } @@ -87,9 +76,9 @@ public class JmxConnectorService extends ServiceSupport implements CamelContextA if (cs != null) { try { cs.stop(); - LOG.debug("Stopped JMX Connector"); + LOG.debug("Stopped Debugger JMX Connector"); } catch (IOException e) { - LOG.debug("Error occurred during stopping JMXConnectorService: {}. This exception will be ignored.", cs); + LOG.debug("Error occurred during stopping CamelDebugger JMX Connector: " + cs + ". This exception will be ignored.", e); } cs = null; } @@ -100,13 +89,12 @@ public class JmxConnectorService extends ServiceSupport implements CamelContextA UnicastRemoteObject.unexportObject(registry, true); LOG.debug("Unexported JMX RMI Registry"); } catch (NoSuchObjectException e) { - LOG.debug("Error occurred while unexporting JMX RMI registry. This exception will be ignored."); + LOG.debug("Error occurred while unexporting JMX RMI registry. This exception will be ignored.", e); } } } protected void createJmxConnector(String host) throws IOException { - int registryPort = DEFAULT_REGISTRY_PORT; String serviceUrlPath = DEFAULT_SERVICE_URL_PATH; int connectorPort = DEFAULT_CONNECTION_PORT; @@ -114,7 +102,7 @@ public class JmxConnectorService extends ServiceSupport implements CamelContextA registry = LocateRegistry.createRegistry(registryPort); LOG.debug("Created JMXConnector RMI registry on port {}", registryPort); } catch (RemoteException ex) { - // The registry may had been created, we could get the registry instead + // The registry may have been created, we could get the registry instead } // must start with leading slash @@ -122,9 +110,10 @@ public class JmxConnectorService extends ServiceSupport implements CamelContextA // Create an RMI connector and start it final JMXServiceURL url; if (connectorPort > 0) { + // we do not allow remote RMI access so this code is disabled url = new JMXServiceURL( "service:jmx:rmi://" + host + ":" + connectorPort + "/jndi/rmi://" + host - + ":" + registryPort + path); + + ":" + registryPort + path); } else { url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://" + host + ":" + registryPort + path); } @@ -133,13 +122,13 @@ public class JmxConnectorService extends ServiceSupport implements CamelContextA // use async thread for starting the JMX Connector // (no need to use a thread pool or enlist in JMX as this thread is terminated when the JMX connector has been started) - Thread thread = getCamelContext().getExecutorServiceManager().newThread("CamelJMXConnector", () -> { + Thread thread = getCamelContext().getExecutorServiceManager().newThread("DebuggerJMXConnector", () -> { try { - LOG.debug("Staring JMX Connector thread to listen at: {}", url); + LOG.debug("Staring Debugger JMX Connector thread to listen at: {}", url); cs.start(); - LOG.info("JMX Connector thread started and listening at: {}", url); - } catch (IOException ioe) { - LOG.warn("Could not start JMXConnector thread at: {}. JMX Connector not in use.", url, ioe); + LOG.info("Debugger JMXConnector listening at: {}", url); + } catch (IOException e) { + LOG.warn("Cannot start Debugger JMX Connector thread at: {}. JMX Connector not in use.", url, e); } }); thread.start(); diff --git a/core/camel-main/src/generated/java/org/apache/camel/main/DebuggerConfigurationPropertiesConfigurer.java b/core/camel-main/src/generated/java/org/apache/camel/main/DebuggerConfigurationPropertiesConfigurer.java index ff3d8dc567f..820b997b6c0 100644 --- a/core/camel-main/src/generated/java/org/apache/camel/main/DebuggerConfigurationPropertiesConfigurer.java +++ b/core/camel-main/src/generated/java/org/apache/camel/main/DebuggerConfigurationPropertiesConfigurer.java @@ -39,6 +39,10 @@ public class DebuggerConfigurationPropertiesConfigurer extends org.apache.camel. case "includeExchangeProperties": target.setIncludeExchangeProperties(property(camelContext, boolean.class, value)); return true; case "includeexchangevariables": case "includeExchangeVariables": target.setIncludeExchangeVariables(property(camelContext, boolean.class, value)); return true; + case "jmxconnectorenabled": + case "jmxConnectorEnabled": target.setJmxConnectorEnabled(property(camelContext, boolean.class, value)); return true; + case "jmxconnectorport": + case "jmxConnectorPort": target.setJmxConnectorPort(property(camelContext, int.class, value)); return true; case "logginglevel": case "loggingLevel": target.setLoggingLevel(property(camelContext, org.apache.camel.LoggingLevel.class, value)); return true; case "singlestepincludestartend": @@ -69,6 +73,10 @@ public class DebuggerConfigurationPropertiesConfigurer extends org.apache.camel. case "includeExchangeProperties": return boolean.class; case "includeexchangevariables": case "includeExchangeVariables": return boolean.class; + case "jmxconnectorenabled": + case "jmxConnectorEnabled": return boolean.class; + case "jmxconnectorport": + case "jmxConnectorPort": return int.class; case "logginglevel": case "loggingLevel": return org.apache.camel.LoggingLevel.class; case "singlestepincludestartend": @@ -100,6 +108,10 @@ public class DebuggerConfigurationPropertiesConfigurer extends org.apache.camel. case "includeExchangeProperties": return target.isIncludeExchangeProperties(); case "includeexchangevariables": case "includeExchangeVariables": return target.isIncludeExchangeVariables(); + case "jmxconnectorenabled": + case "jmxConnectorEnabled": return target.isJmxConnectorEnabled(); + case "jmxconnectorport": + case "jmxConnectorPort": return target.getJmxConnectorPort(); case "logginglevel": case "loggingLevel": return target.getLoggingLevel(); case "singlestepincludestartend": diff --git a/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json b/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json index 6a5e64eabbf..3ed4a3937f0 100644 --- a/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json +++ b/core/camel-main/src/generated/resources/META-INF/camel-main-configuration-metadata.json @@ -145,6 +145,8 @@ { "name": "camel.debug.includeException", "description": "Trace messages to include exception if the message failed", "sourceType": "org.apache.camel.main.DebuggerConfigurationProperties", "type": "boolean", "javaType": "boolean", "defaultValue": true }, { "name": "camel.debug.includeExchangeProperties", "description": "Whether to include the exchange properties in the traced message", "sourceType": "org.apache.camel.main.DebuggerConfigurationProperties", "type": "boolean", "javaType": "boolean", "defaultValue": true }, { "name": "camel.debug.includeExchangeVariables", "description": "Whether to include the exchange variables in the traced message", "sourceType": "org.apache.camel.main.DebuggerConfigurationProperties", "type": "boolean", "javaType": "boolean", "defaultValue": true }, + { "name": "camel.debug.jmxConnectorEnabled", "description": "Whether to create JMX connector that allows tooling to control the Camel debugger. This is what the IDEA and VSCode tooling is using.", "sourceType": "org.apache.camel.main.DebuggerConfigurationProperties", "type": "boolean", "javaType": "boolean", "defaultValue": true }, + { "name": "camel.debug.jmxConnectorPort", "description": "Port number to expose a JMX RMI connector for tooling that needs to control the debugger.", "sourceType": "org.apache.camel.main.DebuggerConfigurationProperties", "type": "integer", "javaType": "int", "defaultValue": 1099 }, { "name": "camel.debug.loggingLevel", "description": "The debugger logging level to use when logging activity.", "sourceType": "org.apache.camel.main.DebuggerConfigurationProperties", "type": "object", "javaType": "org.apache.camel.LoggingLevel", "defaultValue": "INFO", "enum": [ "ERROR", "WARN", "INFO", "DEBUG", "TRACE", "OFF" ] }, { "name": "camel.debug.singleStepIncludeStartEnd", "description": "In single step mode, then when the exchange is created and completed, then simulate a breakpoint at start and end, that allows to suspend and watch the incoming\/complete exchange at the route (you can see message body as response, failed exception etc).", "sourceType": "org.apache.camel.main.DebuggerConfigurationProperties", "type": "boolean", "javaType": "boolean", "defaultValue": "false" }, { "name": "camel.debug.standby", "description": "To set the debugger in standby mode, where the debugger will be installed by not automatic enabled. The debugger can then later be enabled explicit from Java, JMX or tooling.", "sourceType": "org.apache.camel.main.DebuggerConfigurationProperties", "type": "boolean", "javaType": "boolean", "defaultValue": "false" }, diff --git a/core/camel-main/src/main/docs/main.adoc b/core/camel-main/src/main/docs/main.adoc index 9030646a5de..794dfb33e63 100644 --- a/core/camel-main/src/main/docs/main.adoc +++ b/core/camel-main/src/main/docs/main.adoc @@ -188,7 +188,7 @@ The camel.server supports 13 options, which are listed below. === Camel Debugger configurations -The camel.debug supports 13 options, which are listed below. +The camel.debug supports 15 options, which are listed below. [width="100%",cols="2,5,^1,2",options="header"] |=== @@ -202,6 +202,8 @@ The camel.debug supports 13 options, which are listed below. | *camel.debug.includeException* | Trace messages to include exception if the message failed | true | boolean | *camel.debug.includeExchange{zwsp}Properties* | Whether to include the exchange properties in the traced message | true | boolean | *camel.debug.includeExchange{zwsp}Variables* | Whether to include the exchange variables in the traced message | true | boolean +| *camel.debug.jmxConnector{zwsp}Enabled* | Whether to create JMX connector that allows tooling to control the Camel debugger. This is what the IDEA and VSCode tooling is using. | true | boolean +| *camel.debug.jmxConnectorPort* | Port number to expose a JMX RMI connector for tooling that needs to control the debugger. | 1099 | int | *camel.debug.loggingLevel* | The debugger logging level to use when logging activity. | INFO | LoggingLevel | *camel.debug.singleStepInclude{zwsp}StartEnd* | In single step mode, then when the exchange is created and completed, then simulate a breakpoint at start and end, that allows to suspend and watch the incoming/complete exchange at the route (you can see message body as response, failed exception etc). | false | boolean | *camel.debug.standby* | To set the debugger in standby mode, where the debugger will be installed by not automatic enabled. The debugger can then later be enabled explicit from Java, JMX or tooling. | false | boolean diff --git a/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java b/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java index 52f5bb07217..fd3ffdd137d 100644 --- a/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java +++ b/core/camel-main/src/main/java/org/apache/camel/main/BaseMainSupport.java @@ -51,6 +51,7 @@ import org.apache.camel.console.DevConsoleRegistry; import org.apache.camel.health.HealthCheck; import org.apache.camel.health.HealthCheckRegistry; import org.apache.camel.health.HealthCheckRepository; +import org.apache.camel.impl.debugger.DebuggerJmxConnectorService; import org.apache.camel.impl.debugger.DefaultBacklogDebugger; import org.apache.camel.impl.engine.DefaultCompileStrategy; import org.apache.camel.impl.engine.DefaultRoutesLoader; @@ -1650,9 +1651,17 @@ public abstract class BaseMainSupport extends BaseService { debugger.setIncludeExchangeVariables(config.isIncludeExchangeVariables()); debugger.setIncludeException(config.isIncludeException()); debugger.setLoggingLevel(config.getLoggingLevel().name()); - debugger.setSuspendMode(config.isWaitForAttach()); + debugger.setSuspendMode(config.isWaitForAttach()); // this option is named wait-for-attach debugger.setFallbackTimeout(config.getFallbackTimeout()); + // enable jmx connector if port is set + if (config.isJmxConnectorEnabled()) { + DebuggerJmxConnectorService connector = new DebuggerJmxConnectorService(); + connector.setCreateConnector(true); + connector.setRegistryPort(config.getJmxConnectorPort()); + camelContext.addService(connector); + } + // start debugger after context is started camelContext.addLifecycleStrategy(new LifecycleStrategySupport() { @Override diff --git a/core/camel-main/src/main/java/org/apache/camel/main/DebuggerConfigurationProperties.java b/core/camel-main/src/main/java/org/apache/camel/main/DebuggerConfigurationProperties.java index 931ce75f930..711a650761d 100644 --- a/core/camel-main/src/main/java/org/apache/camel/main/DebuggerConfigurationProperties.java +++ b/core/camel-main/src/main/java/org/apache/camel/main/DebuggerConfigurationProperties.java @@ -55,6 +55,10 @@ public class DebuggerConfigurationProperties implements BootstrapCloseable { private boolean includeException = true; @Metadata(label = "advanced", defaultValue = "300") private long fallbackTimeout = 300; + @Metadata(label = "advanced", defaultValue = "true") + private boolean jmxConnectorEnabled = true; + @Metadata(label = "advanced", defaultValue = "1099") + private int jmxConnectorPort = 1099; public DebuggerConfigurationProperties(MainConfigurationProperties parent) { this.parent = parent; @@ -221,6 +225,29 @@ public class DebuggerConfigurationProperties implements BootstrapCloseable { this.fallbackTimeout = fallbackTimeout; } + public boolean isJmxConnectorEnabled() { + return jmxConnectorEnabled; + } + + /** + * Whether to create JMX connector that allows tooling to control the Camel debugger. + * This is what the IDEA and VSCode tooling is using. + */ + public void setJmxConnectorEnabled(boolean jmxConnectorEnabled) { + this.jmxConnectorEnabled = jmxConnectorEnabled; + } + + public int getJmxConnectorPort() { + return jmxConnectorPort; + } + + /** + * Port number to expose a JMX RMI connector for tooling that needs to control the debugger. + */ + public void setJmxConnectorPort(int jmxConnectorPort) { + this.jmxConnectorPort = jmxConnectorPort; + } + /** * Enables Debugger in your Camel application. */ @@ -334,4 +361,21 @@ public class DebuggerConfigurationProperties implements BootstrapCloseable { return this; } + /** + * Whether to create JMX connector that allows tooling to control the Camel debugger. + * This is what the IDEA and VSCode tooling is using. + */ + public DebuggerConfigurationProperties withJmxConnectorEnabled(boolean jmxConnectorEnabled) { + this.jmxConnectorEnabled = jmxConnectorEnabled; + return this; + } + + /** + * Port number to expose a JMX RMI connector for tooling that needs to control the debugger. + */ + public DebuggerConfigurationProperties withJmxConnectorPort(int jmxConnectorPort) { + this.jmxConnectorPort = jmxConnectorPort; + return this; + } + }