This is an automated email from the ASF dual-hosted git repository. jamesnetherton pushed a commit to branch camel-main in repository https://gitbox.apache.org/repos/asf/camel-quarkus.git
commit d31ba7659d5b39db47ae239d74edb6d62b61110a Author: James Netherton <jamesnether...@gmail.com> AuthorDate: Tue May 21 14:45:47 2024 +0100 Handle DebuggerJmxConnectorService being present in the camel core --- .../core/deployment/CamelDebugProcessor.java | 46 +++++++++++++++ .../DebuggerJmxConnectorServiceSubstitutions.java | 67 ++++++++++++++++++++++ .../camel/quarkus/main/CoreMainResource.java | 31 ++++++++++ .../main/src/main/resources/application.properties | 2 + .../apache/camel/quarkus/main/CoreMainTest.java | 18 ++++++ .../quarkus/component/management/it/Routes.java | 2 + .../src/main/resources/application.properties | 3 + .../component/management/it/ManagementTest.java | 14 +++++ 8 files changed, 183 insertions(+) diff --git a/extensions-core/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/CamelDebugProcessor.java b/extensions-core/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/CamelDebugProcessor.java new file mode 100644 index 0000000000..5ee593c439 --- /dev/null +++ b/extensions-core/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/CamelDebugProcessor.java @@ -0,0 +1,46 @@ +/* + * 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.quarkus.core.deployment; + +import java.util.function.BooleanSupplier; +import java.util.stream.StreamSupport; + +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.annotations.BuildSteps; +import io.quarkus.deployment.builditem.AllowJNDIBuildItem; +import org.eclipse.microprofile.config.ConfigProvider; + +/** + * Build steps relating to camel debugging support. This is primarily required due to camel-main + * having the capability to enable debugging features that live in the Camel core such as the + * DebuggerJmxConnectorService + */ +@BuildSteps(onlyIf = CamelDebugProcessor.CamelDebugConfigurationPresent.class) +public class CamelDebugProcessor { + @BuildStep + AllowJNDIBuildItem allowJNDI() { + return new AllowJNDIBuildItem(); + } + + static final class CamelDebugConfigurationPresent implements BooleanSupplier { + @Override + public boolean getAsBoolean() { + return StreamSupport.stream(ConfigProvider.getConfig().getPropertyNames().spliterator(), false) + .anyMatch(key -> key.startsWith("camel.debug")); + } + } +} diff --git a/extensions-core/core/runtime/src/main/java/org/apache/camel/quarkus/core/graal/DebuggerJmxConnectorServiceSubstitutions.java b/extensions-core/core/runtime/src/main/java/org/apache/camel/quarkus/core/graal/DebuggerJmxConnectorServiceSubstitutions.java new file mode 100644 index 0000000000..866bcb7f89 --- /dev/null +++ b/extensions-core/core/runtime/src/main/java/org/apache/camel/quarkus/core/graal/DebuggerJmxConnectorServiceSubstitutions.java @@ -0,0 +1,67 @@ +/* + * 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.quarkus.core.graal; + +import java.io.IOException; +import java.util.function.BooleanSupplier; + +import com.oracle.svm.core.annotate.Alias; +import com.oracle.svm.core.annotate.RecomputeFieldValue; +import com.oracle.svm.core.annotate.RecomputeFieldValue.Kind; +import com.oracle.svm.core.annotate.Substitute; +import com.oracle.svm.core.annotate.TargetClass; +import org.apache.camel.impl.debugger.DebuggerJmxConnectorService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Disable MBean server interactions in DebuggerJmxConnectorService if camel-management is not present. + */ +@TargetClass(value = DebuggerJmxConnectorService.class, onlyWith = CamelManagementAbsent.class) +final class DebuggerJmxConnectorServiceSubstitutions { + @Alias + @RecomputeFieldValue(kind = Kind.FromAlias) + private static Logger LOG = LoggerFactory.getLogger(DebuggerJmxConnectorService.class); + + @Substitute + protected void doStart() throws Exception { + LOG.warn( + "The JmxConnectorService is enabled but camel-quarkus-management is not detected. DebuggerJmxConnectorService will not be started."); + } + + @Substitute + protected void doStop() throws Exception { + // Noop + } + + @Substitute + protected void createJmxConnector(String host) throws IOException { + // Noop + } +} + +final class CamelManagementAbsent implements BooleanSupplier { + @Override + public boolean getAsBoolean() { + try { + Thread.currentThread().getContextClassLoader().loadClass("org.apache.camel.management.ManagedCamelContextImpl"); + return false; + } catch (ClassNotFoundException e) { + return true; + } + } +} diff --git a/integration-tests/main/src/main/java/org/apache/camel/quarkus/main/CoreMainResource.java b/integration-tests/main/src/main/java/org/apache/camel/quarkus/main/CoreMainResource.java index 1652f2d49a..5b369df9ce 100644 --- a/integration-tests/main/src/main/java/org/apache/camel/quarkus/main/CoreMainResource.java +++ b/integration-tests/main/src/main/java/org/apache/camel/quarkus/main/CoreMainResource.java @@ -16,6 +16,9 @@ */ package org.apache.camel.quarkus.main; +import java.io.IOException; +import java.net.ConnectException; +import java.net.Socket; import java.util.HashMap; import java.util.Map; import java.util.Objects; @@ -34,7 +37,9 @@ import jakarta.ws.rs.Produces; import jakarta.ws.rs.core.MediaType; import org.apache.camel.CamelContext; import org.apache.camel.Component; +import org.apache.camel.ServiceStatus; import org.apache.camel.component.log.LogComponent; +import org.apache.camel.impl.debugger.DebuggerJmxConnectorService; import org.apache.camel.model.ModelCamelContext; import org.apache.camel.quarkus.core.FastFactoryFinderResolver; import org.apache.camel.quarkus.it.support.typeconverter.MyPair; @@ -251,4 +256,30 @@ public class CoreMainResource { final DefaultRegistry registry = main.getCamelContext().getRegistry(DefaultRegistry.class); return registry.getFallbackRegistry().lookupByNameAndType(name, String.class); } + + @Path("/service/jmx-connector/status") + @GET + @Produces(MediaType.TEXT_PLAIN) + public String getJmxConnectorServiceStatus() { + DebuggerJmxConnectorService service = main.getCamelContext().hasService(DebuggerJmxConnectorService.class); + if (service == null) { + return ServiceStatus.Stopped.name().toUpperCase(); + } + return service.getStatus().name().toUpperCase(); + } + + @Path("/service/jmx-connector/expected/connect/state") + @GET + @Produces(MediaType.TEXT_PLAIN) + public boolean jmxConnectorServiceAvailabilityIsExpectedState() { + boolean isNativeMode = "executable".equals(System.getProperty("org.graalvm.nativeimage.kind")); + try (Socket socket = new Socket("localhost", DebuggerJmxConnectorService.DEFAULT_REGISTRY_PORT)) { + return true; + } catch (IOException e) { + // Since camel-quarkus-management is not on the classpath + // native mode will replace DebuggerJmxConnectorService methods with noop implementations + // hence the expected state is that we should not be able to connect + return isNativeMode && e instanceof ConnectException; + } + } } diff --git a/integration-tests/main/src/main/resources/application.properties b/integration-tests/main/src/main/resources/application.properties index c5c221fa62..ad3438bc34 100644 --- a/integration-tests/main/src/main/resources/application.properties +++ b/integration-tests/main/src/main/resources/application.properties @@ -32,6 +32,8 @@ camel.resilience4j.sliding-window-size = 1234 # camel.main.auto-configuration-log-summary = false +# To verify DebuggerJmxConnectorService substitutions take effect due to lack of camel-quarkus-management on the classpath +camel.debug.enabled = true # # Other diff --git a/integration-tests/main/src/test/java/org/apache/camel/quarkus/main/CoreMainTest.java b/integration-tests/main/src/test/java/org/apache/camel/quarkus/main/CoreMainTest.java index 5eb958bd2f..125726ed7e 100644 --- a/integration-tests/main/src/test/java/org/apache/camel/quarkus/main/CoreMainTest.java +++ b/integration-tests/main/src/test/java/org/apache/camel/quarkus/main/CoreMainTest.java @@ -25,6 +25,7 @@ import io.restassured.http.ContentType; import io.restassured.path.json.JsonPath; import io.restassured.response.Response; import jakarta.ws.rs.core.MediaType; +import org.apache.camel.ServiceStatus; import org.apache.camel.quarkus.core.DisabledModelToXMLDumper; import org.apache.camel.quarkus.core.RegistryRoutesLoaders; import org.apache.camel.quarkus.it.support.mainlistener.CustomMainListener; @@ -207,4 +208,21 @@ public class CoreMainTest { .statusCode(200) .body(is("String From Registry")); } + + @Test + public void testJmxConnectorService() { + RestAssured.given() + .accept(MediaType.TEXT_PLAIN) + .get("/test/service/jmx-connector/status") + .then() + .statusCode(200) + .body(is(ServiceStatus.Started.name().toUpperCase())); + + RestAssured.given() + .accept(MediaType.TEXT_PLAIN) + .get("/test/service/jmx-connector/expected/connect/state") + .then() + .statusCode(200) + .body(is("true")); + } } diff --git a/integration-tests/management/src/main/java/org/apache/camel/quarkus/component/management/it/Routes.java b/integration-tests/management/src/main/java/org/apache/camel/quarkus/component/management/it/Routes.java index 7f2aa5ece9..9b57e78a6b 100644 --- a/integration-tests/management/src/main/java/org/apache/camel/quarkus/component/management/it/Routes.java +++ b/integration-tests/management/src/main/java/org/apache/camel/quarkus/component/management/it/Routes.java @@ -21,6 +21,8 @@ import org.apache.camel.builder.RouteBuilder; public class Routes extends RouteBuilder { @Override public void configure() throws Exception { + getContext().setDebugging(false); + from("direct:start").routeId("hello").setBody().constant("Hello World"); from("direct:count").routeId("count") diff --git a/integration-tests/management/src/main/resources/application.properties b/integration-tests/management/src/main/resources/application.properties index 8df9285f2f..f136dbd57b 100644 --- a/integration-tests/management/src/main/resources/application.properties +++ b/integration-tests/management/src/main/resources/application.properties @@ -18,3 +18,6 @@ quarkus.native.monitoring=jmxserver # Required to update routes via JMX camel.main.jmxUpdateRouteEnabled = true + +# To test DebuggerJmxConnectorService provided by Camel core +camel.debug.enabled = true \ No newline at end of file diff --git a/integration-tests/management/src/test/java/org/apache/camel/quarkus/component/management/it/ManagementTest.java b/integration-tests/management/src/test/java/org/apache/camel/quarkus/component/management/it/ManagementTest.java index f5f9145e0d..1c5c944132 100644 --- a/integration-tests/management/src/test/java/org/apache/camel/quarkus/component/management/it/ManagementTest.java +++ b/integration-tests/management/src/test/java/org/apache/camel/quarkus/component/management/it/ManagementTest.java @@ -16,14 +16,19 @@ */ package org.apache.camel.quarkus.component.management.it; +import java.io.IOException; +import java.net.Socket; + import io.quarkus.test.junit.QuarkusTest; import io.restassured.RestAssured; +import org.apache.camel.impl.debugger.DebuggerJmxConnectorService; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertTrue; @QuarkusTest class ManagementTest { @@ -158,4 +163,13 @@ class ManagementTest { .statusCode(200) .body(is("2")); } + + @Test + public void jmxConnectorService() { + try (Socket socket = new Socket("localhost", DebuggerJmxConnectorService.DEFAULT_REGISTRY_PORT)) { + assertTrue(socket.isConnected()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } }