This is an automated email from the ASF dual-hosted git repository. jamesnetherton pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel-quarkus.git
The following commit(s) were added to refs/heads/main by this push: new a6efc8ebf6 Add Jolokia JVM only extension a6efc8ebf6 is described below commit a6efc8ebf6b07d471e841efcb3389cc6b5f8c732 Author: James Netherton <jamesnether...@gmail.com> AuthorDate: Mon Feb 10 07:33:42 2025 +0000 Add Jolokia JVM only extension Fixes #6942 --- .github/dependabot.yml | 1 + catalog/pom.xml | 13 + docs/modules/ROOT/nav.adoc | 1 + .../ROOT/pages/reference/extensions/jolokia.adoc | 266 +++++++++++++++++++++ extensions-jvm/jolokia/deployment/pom.xml | 90 +++++++ .../jolokia/deployment/JolokiaProcessor.java | 156 ++++++++++++ .../jolokia/deployment/JolokiaServerBuildItem.java | 33 +++ .../deployment/JolokiaServerConfigBuildItem.java | 33 +++ .../jolokia/JolokiaCustomContextPathTest.java | 44 ++++ .../camel/quarkus/jolokia/JolokiaDisabledTest.java | 38 +++ .../camel/quarkus/jolokia/JolokiaEnabledTest.java | 90 +++++++ .../JolokiaManagementEndpointDisabledTest.java | 36 +++ extensions-jvm/jolokia/pom.xml | 37 +++ extensions-jvm/jolokia/runtime/pom.xml | 88 +++++++ .../jolokia/runtime/src/main/doc/usage.adoc | 106 ++++++++ .../jolokia/CamelQuarkusJolokiaLogHandler.java | 44 ++++ .../quarkus/jolokia/CamelQuarkusJolokiaServer.java | 35 +++ .../camel/quarkus/jolokia/JolokiaRecorder.java | 179 ++++++++++++++ .../jolokia/JolokiaRequestRedirectHandler.java | 44 ++++ .../jolokia/config/JolokiaBuildTimeConfig.java | 63 +++++ .../jolokia/config/JolokiaRuntimeConfig.java | 118 +++++++++ .../jolokia/restrictor/CamelJolokiaRestrictor.java | 51 ++++ .../main/resources/META-INF/quarkus-extension.yaml | 34 +++ extensions-jvm/pom.xml | 1 + integration-tests-jvm/jolokia/pom.xml | 132 ++++++++++ .../component/jolokia/it/JolokiaResource.java | 56 +++++ .../component/jolokia/it/JolokiaRoutes.java | 27 +++ .../it/JolokiaAdditionalPropertiesTest.java | 53 ++++ .../jolokia/it/JolokiaCustomHostPortTest.java | 57 +++++ .../jolokia/it/JolokiaDisableAutoStartTest.java | 91 +++++++ .../jolokia/it/JolokiaDisableRestrictorTest.java | 53 ++++ .../jolokia/it/JolokiaDiscoveryDisabledTest.java | 57 +++++ .../jolokia/it/JolokiaKubernetesClientSSLTest.java | 93 +++++++ .../quarkus/component/jolokia/it/JolokiaTest.java | 67 ++++++ integration-tests-jvm/pom.xml | 1 + pom.xml | 1 + poms/bom/pom.xml | 15 ++ poms/bom/src/main/generated/flattened-full-pom.xml | 15 ++ .../src/main/generated/flattened-reduced-pom.xml | 15 ++ .../generated/flattened-reduced-verbose-pom.xml | 15 ++ 40 files changed, 2349 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 38414472ab..1c73bf485d 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -42,6 +42,7 @@ updates: - dependency-name: "com.squareup.okhttp3:okhttp" - dependency-name: "io.swagger.codegen.v3:swagger-codegen-generators" - dependency-name: "org.amqphub.quarkus:quarkus-qpid-jms-bom" + - dependency-name: "org.jolokia:jolokia-agent-jvm" # Test dependencies - dependency-name: "org.wiremock:wiremock-standalone" - dependency-name: "com.unboundid:unboundid-ldapsdk" diff --git a/catalog/pom.xml b/catalog/pom.xml index efb8d130f4..ebe892d5b9 100644 --- a/catalog/pom.xml +++ b/catalog/pom.xml @@ -2086,6 +2086,19 @@ </exclusion> </exclusions> </dependency> + <dependency> + <groupId>org.apache.camel.quarkus</groupId> + <artifactId>camel-quarkus-jolokia</artifactId> + <version>${project.version}</version> + <type>pom</type> + <scope>test</scope> + <exclusions> + <exclusion> + <groupId>*</groupId> + <artifactId>*</artifactId> + </exclusion> + </exclusions> + </dependency> <dependency> <groupId>org.apache.camel.quarkus</groupId> <artifactId>camel-quarkus-jolt</artifactId> diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc index 15fce24d7a..ee6196da74 100644 --- a/docs/modules/ROOT/nav.adoc +++ b/docs/modules/ROOT/nav.adoc @@ -183,6 +183,7 @@ *** xref:reference/extensions/javascript.adoc[JavaScript] *** xref:reference/extensions/jfr.adoc[Jfr] *** xref:reference/extensions/jira.adoc[Jira] +*** xref:reference/extensions/jolokia.adoc[Jolokia] *** xref:reference/extensions/json-patch.adoc[JsonPatch] *** xref:reference/extensions/kafka.adoc[Kafka] *** xref:reference/extensions/kamelet.adoc[Kamelet] diff --git a/docs/modules/ROOT/pages/reference/extensions/jolokia.adoc b/docs/modules/ROOT/pages/reference/extensions/jolokia.adoc new file mode 100644 index 0000000000..67d3cf7c3f --- /dev/null +++ b/docs/modules/ROOT/pages/reference/extensions/jolokia.adoc @@ -0,0 +1,266 @@ +// Do not edit directly! +// This file was generated by camel-quarkus-maven-plugin:update-extension-doc-page +[id="extensions-jolokia"] += Jolokia +:linkattrs: +:cq-artifact-id: camel-quarkus-jolokia +:cq-native-supported: false +:cq-status: Preview +:cq-status-deprecation: Preview +:cq-description: Expose runtime metrics and management operations via JMX with Jolokia +:cq-deprecated: false +:cq-jvm-since: 3.19.0 +:cq-native-since: n/a + +ifeval::[{doc-show-badges} == true] +[.badges] +[.badge-key]##JVM since##[.badge-supported]##3.19.0## [.badge-key]##Native##[.badge-unsupported]##unsupported## +endif::[] + +Expose runtime metrics and management operations via JMX with Jolokia + +[id="extensions-jolokia-maven-coordinates"] +== Maven coordinates + +[source,xml] +---- +<dependency> + <groupId>org.apache.camel.quarkus</groupId> + <artifactId>camel-quarkus-jolokia</artifactId> +</dependency> +---- +ifeval::[{doc-show-user-guide-link} == true] +Check the xref:user-guide/index.adoc[User guide] for more information about writing Camel Quarkus applications. +endif::[] + +[id="extensions-jolokia-usage"] +== Usage +This extension adds https://jolokia.org/[Jolokia] support to your application. + +[id="extensions-jolokia-usage-jolokia-http-endpoints"] +=== Jolokia HTTP endpoints + +In prod mode, Jolokia is accessible at the following URLs. + +* http://0.0.0.0:8778/jolokia/ +* http://0.0.0.0:8080/q/jolokia + +In dev and test modes Jolokia is bound only to `localhost`. + +To disable exposing Jolokia via the Quarkus management interface at `/q/jolokia`, add the following configuration to `application.properties`. + +[source] +---- +quarkus.camel.jolokia.register-management-endpoint=false +---- + +If you want to disable Jolokia entirely, then add the following configuration to `application.properties`. + +[source] +---- +quarkus.camel.jolokia.enabled=false +---- + +[id="extensions-jolokia-usage-jolokia-configuration"] +=== Jolokia configuration + +Any of the https://jolokia.org/reference/html/manual/agents.html[Jolokia configuration options] can be configured via the `quarkus.camel.jolokia.additional-properties.<jolokia-property-name>` option. +Where `<jolokia-property-name>` is the name of the Jolokia configuration option you want to set. + +For example, the following configuration added to `application.properties` enables Jolokia debugging and sets the max depth for traversing bean properties. + +[source] +---- +quarkus.camel.jolokia.additional-properties.debug=true +quarkus.camel.jolokia.additional-properties.maxDepth=10 +---- + +[id="extensions-jolokia-usage-jolokia-restrictor"] +=== Jolokia restrictor + +By default, a Jolokia restrictor is automatically registered that exposes access to only a specific set of MBean domains. + +* `org.apache.camel` +* `java.lang` +* `java.nio` + +If this is too restrictive, then you can either disable the default restrictor, or create your own custom restrictor. + +[id="extensions-jolokia-usage-disable-the-default-restrictor"] +==== Disable the default restrictor + +The following configuration added to `application.properties` disables the default restrictor. + +[source] +---- +quarkus.camel.jolokia.register-camel-restrictor=false +---- + +[id="extensions-jolokia-usage-create-a-custom-restrictor"] +==== Create a custom restrictor + +You can create your own restrictor class and register it with Jolokia. + +[source,java] +---- +public class CustomRestrictor extends AllowAllRestrictor { + // Override methods to apply custom restrictions +} +---- + +Register the restrictor with Jolokia by adding the following configuration to `application.properties`. + +[source] +---- +quarkus.camel.jolokia.additional-properties.restrictorClass=org.acme.CustomRestrictor +---- + +[id="extensions-jolokia-usage-kubernetes-openshift-support"] +=== Kubernetes & OpenShift support + +[id="extensions-jolokia-usage-generated-kubernetes-manifests"] +==== Generated Kubernetes manifests + +If the `quarkus-kubernetes` or `quarkus-openshift` extensions are present, a container port named `jolokia` will be added automatically to the pod configuration within the generated Kubernetes manifest resources. + +This can be disabled by adding the following configuration to `application.properties`. + +[source] +---- +quarkus.camel.jolokia.kubernetes.expose-container-port=false +---- + +[id="extensions-jolokia-usage-automatic-enablement-of-ssl-client-authentication"] +==== Automatic enablement of SSL client authentication + +If the application detects that it is running on Kubernetes or OpenShift, then Jolokia is automatically configured for SSL client authentication. +This is useful if you use tools like https://hawt.io/[Hawtio] to discover and connect to your running application pod. + +This functionality can be disabled by adding the following configuration to `application.properties`. + +[source] +---- +quarkus.camel.jolokia.kubernetes.client-authentication-enabled=false +---- + +Note that if you choose to use https://github.com/hawtio/hawtio-online[hawtio-online] to connect to your running application, then you must configure the Jolokia client principal. + +[source] +---- +quarkus.camel.jolokia.kubernetes.client-principal="cn=hawtio-online.hawtio.svc" +---- + + +[id="extensions-jolokia-additional-camel-quarkus-configuration"] +== Additional Camel Quarkus configuration + +[width="100%",cols="80,5,15",options="header"] +|=== +| Configuration property | Type | Default + + +|icon:lock[title=Fixed at build time] [[quarkus.camel.jolokia.enabled]]`link:#quarkus.camel.jolokia.enabled[quarkus.camel.jolokia.enabled]` + +Enables Jolokia support. +| `boolean` +| `true` + +|icon:lock[title=Fixed at build time] [[quarkus.camel.jolokia.path]]`link:#quarkus.camel.jolokia.path[quarkus.camel.jolokia.path]` + +The context path that the Jolokia agent is deployed under. +| `string` +| `jolokia` + +|icon:lock[title=Fixed at build time] [[quarkus.camel.jolokia.register-management-endpoint]]`link:#quarkus.camel.jolokia.register-management-endpoint[quarkus.camel.jolokia.register-management-endpoint]` + +Whether to register a Quarkus management endpoint for Jolokia (default /q/jolokia). +When enabled this activates a management endpoint which will be accessible on a path relative to +${quarkus.http.non-application-root-path}/${quarkus.camel.jolokia.server.path}. +If the management interface is enabled, the value will be resolved as a path relative to +${quarkus.management.root-path}/${quarkus.camel.jolokia.server.path}. Note that for this feature to work you must +have quarkus-vertx-http on the application classpath. +| `boolean` +| `true` + +|icon:lock[title=Fixed at build time] [[quarkus.camel.jolokia.kubernetes.expose-container-port]]`link:#quarkus.camel.jolokia.kubernetes.expose-container-port[quarkus.camel.jolokia.kubernetes.expose-container-port]` + +When {@code true} and the quarkus-kubernetes extension is present, a container port named jolokia will +be added to the generated Kubernetes manifests within the container spec ports definition. +| `boolean` +| `true` + +| [[quarkus.camel.jolokia.server.auto-start]]`link:#quarkus.camel.jolokia.server.auto-start[quarkus.camel.jolokia.server.auto-start]` + +Whether the Jolokia agent HTTP server should be started automatically. +When set to {@code false}, it is the user responsibility to start the server. +This can be done via {@code @Inject CamelQuarkusJolokiaServer} and then invoking the start() method. +| `boolean` +| `true` + +| [[quarkus.camel.jolokia.server.host]]`link:#quarkus.camel.jolokia.server.host[quarkus.camel.jolokia.server.host]` + +The host address to which the Jolokia agent HTTP server should bind to. +When unspecified, the default is localhost for dev and test mode. +In prod mode the default is to bind to all interfaces at 0.0.0.0. +| `string` +| + +| [[quarkus.camel.jolokia.server.port]]`link:#quarkus.camel.jolokia.server.port[quarkus.camel.jolokia.server.port]` + +The port on which the Jolokia agent HTTP server should listen on. +| `int` +| `8778` + +| [[quarkus.camel.jolokia.server.discovery-enabled-mode]]`link:#quarkus.camel.jolokia.server.discovery-enabled-mode[quarkus.camel.jolokia.server.discovery-enabled-mode]` + +The mode in which Jolokia agent discovery is enabled. The default {@code dev-test}, enables discovery only in dev and +test modes. +A value of {@code all} enables agent discovery in dev, test and prod modes. Setting the value to {@code none} will +disable agent discovery in all modes. +| `all`, `dev-test`, `none` +| `dev-test` + +| [[quarkus.camel.jolokia.kubernetes.client-authentication-enabled]]`link:#quarkus.camel.jolokia.kubernetes.client-authentication-enabled[quarkus.camel.jolokia.kubernetes.client-authentication-enabled]` + +Whether to enable Jolokia SSL client authentication in Kubernetes environments. +Useful for tools such as hawtio to be able to connect with your application. +| `boolean` +| `true` + +| [[quarkus.camel.jolokia.kubernetes.service-ca-cert]]`link:#quarkus.camel.jolokia.kubernetes.service-ca-cert[quarkus.camel.jolokia.kubernetes.service-ca-cert]` + +Absolute path of the CA certificate Jolokia should use for SSL client authentication. +| link:https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/io/File.html[`File`] +| `/var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt` + +| [[quarkus.camel.jolokia.kubernetes.client-principal]]`link:#quarkus.camel.jolokia.kubernetes.client-principal[quarkus.camel.jolokia.kubernetes.client-principal]` + +The principal which must be given in a client certificate to allow access to Jolokia. +| `string` +| + +| [[quarkus.camel.jolokia.additional-properties.-additional-properties]]`link:#quarkus.camel.jolokia.additional-properties.-additional-properties[quarkus.camel.jolokia.additional-properties."additional-properties"]` + +Arbitrary Jolokia configuration options. These are described at the +<a href="https://jolokia.org/reference/html/manual/agents.html">Jolokia documentation</a>. +Options can be configured like {@code quarkus.camel.jolokia.additional-properties."debug"=true}. +| `Map<String,String>` +| + +| [[quarkus.camel.jolokia.register-camel-restrictor]]`link:#quarkus.camel.jolokia.register-camel-restrictor[quarkus.camel.jolokia.register-camel-restrictor]` + +When {@code true}, a Jolokia restrictor is registered that limits MBean read, write and operation execution to the +following MBean domains. +<ul> +<li>org.apache.camel</li> +<li>java.lang</li> +<li>java.nio</li> +</ul> +Note that this option has no effect if quarkus.camel.jolokia.additional-properties."restrictorClass" is set. +| `boolean` +| `true` +|=== + +[.configuration-legend] +{doc-link-icon-lock}[title=Fixed at build time] Configuration property fixed at build time. All other configuration properties are overridable at runtime. + diff --git a/extensions-jvm/jolokia/deployment/pom.xml b/extensions-jvm/jolokia/deployment/pom.xml new file mode 100644 index 0000000000..78960c937a --- /dev/null +++ b/extensions-jvm/jolokia/deployment/pom.xml @@ -0,0 +1,90 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + 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. + +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.apache.camel.quarkus</groupId> + <artifactId>camel-quarkus-jolokia-parent</artifactId> + <version>3.19.0-SNAPSHOT</version> + <relativePath>../pom.xml</relativePath> + </parent> + + <artifactId>camel-quarkus-jolokia-deployment</artifactId> + <name>Camel Quarkus :: Jolokia :: Deployment</name> + + <dependencies> + <dependency> + <groupId>io.quarkus</groupId> + <artifactId>quarkus-vertx-http-deployment</artifactId> + </dependency> + <dependency> + <groupId>io.quarkus</groupId> + <artifactId>quarkus-kubernetes-spi</artifactId> + </dependency> + <dependency> + <groupId>org.apache.camel.quarkus</groupId> + <artifactId>camel-quarkus-management-deployment</artifactId> + </dependency> + <dependency> + <groupId>org.apache.camel.quarkus</groupId> + <artifactId>camel-quarkus-jolokia</artifactId> + </dependency> + + <dependency> + <groupId>io.quarkus</groupId> + <artifactId>quarkus-junit5-internal</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>io.rest-assured</groupId> + <artifactId>rest-assured</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-direct</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.camel</groupId> + <artifactId>camel-seda</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <configuration> + <annotationProcessorPaths> + <path> + <groupId>io.quarkus</groupId> + <artifactId>quarkus-extension-processor</artifactId> + <version>${quarkus.version}</version> + </path> + </annotationProcessorPaths> + </configuration> + </plugin> + </plugins> + </build> + +</project> diff --git a/extensions-jvm/jolokia/deployment/src/main/java/org/apache/camel/quarkus/jolokia/deployment/JolokiaProcessor.java b/extensions-jvm/jolokia/deployment/src/main/java/org/apache/camel/quarkus/jolokia/deployment/JolokiaProcessor.java new file mode 100644 index 0000000000..8e3b026d8d --- /dev/null +++ b/extensions-jvm/jolokia/deployment/src/main/java/org/apache/camel/quarkus/jolokia/deployment/JolokiaProcessor.java @@ -0,0 +1,156 @@ +/* + * 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.jolokia.deployment; + +import java.util.function.BooleanSupplier; + +import io.quarkus.arc.deployment.SyntheticBeanBuildItem; +import io.quarkus.deployment.Capabilities; +import io.quarkus.deployment.Capability; +import io.quarkus.deployment.IsDevelopment; +import io.quarkus.deployment.IsNormal; +import io.quarkus.deployment.annotations.BuildProducer; +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.annotations.BuildSteps; +import io.quarkus.deployment.annotations.ExecutionTime; +import io.quarkus.deployment.annotations.Record; +import io.quarkus.deployment.builditem.FeatureBuildItem; +import io.quarkus.deployment.builditem.ShutdownContextBuildItem; +import io.quarkus.deployment.pkg.steps.NativeOrNativeSourcesBuild; +import io.quarkus.kubernetes.spi.KubernetesPortBuildItem; +import io.quarkus.vertx.http.deployment.BodyHandlerBuildItem; +import io.quarkus.vertx.http.deployment.NonApplicationRootPathBuildItem; +import io.quarkus.vertx.http.deployment.RouteBuildItem; +import jakarta.enterprise.context.ApplicationScoped; +import org.apache.camel.quarkus.core.JvmOnlyRecorder; +import org.apache.camel.quarkus.jolokia.CamelQuarkusJolokiaServer; +import org.apache.camel.quarkus.jolokia.JolokiaRecorder; +import org.apache.camel.quarkus.jolokia.config.JolokiaBuildTimeConfig; +import org.apache.camel.quarkus.jolokia.config.JolokiaRuntimeConfig; +import org.jboss.logging.Logger; + +@BuildSteps(onlyIf = JolokiaProcessor.JolokiaEnabled.class) +public class JolokiaProcessor { + private static final Logger LOG = Logger.getLogger(JolokiaProcessor.class); + private static final String FEATURE = "camel-jolokia"; + + @BuildStep + FeatureBuildItem feature() { + return new FeatureBuildItem(FEATURE); + } + + @BuildStep + @Record(ExecutionTime.RUNTIME_INIT) + JolokiaServerConfigBuildItem createJolokiaServerConfig( + JolokiaBuildTimeConfig buildTimeConfig, + JolokiaRuntimeConfig config, + JolokiaRecorder recorder) { + return new JolokiaServerConfigBuildItem(recorder.createJolokiaServerConfig(config, buildTimeConfig.path())); + } + + @BuildStep + @Record(ExecutionTime.RUNTIME_INIT) + JolokiaServerBuildItem createJolokiaServer( + JolokiaServerConfigBuildItem jolokiaServerConfig, + JolokiaRecorder recorder) { + return new JolokiaServerBuildItem(recorder.createJolokiaServer(jolokiaServerConfig.getRuntimeValue())); + } + + @BuildStep + @Record(ExecutionTime.RUNTIME_INIT) + void startJolokiaServer( + JolokiaServerBuildItem jolokiaServer, + JolokiaRuntimeConfig runtimeConfig, + BuildProducer<SyntheticBeanBuildItem> syntheticBean, + JolokiaRecorder recorder) { + recorder.startJolokiaServer(jolokiaServer.getRuntimeValue(), runtimeConfig); + syntheticBean.produce(SyntheticBeanBuildItem.configure(CamelQuarkusJolokiaServer.class) + .scope(ApplicationScoped.class) + .runtimeValue(recorder.createJolokiaServerBean(jolokiaServer.getRuntimeValue())) + .setRuntimeInit() + .done()); + } + + @BuildStep(onlyIfNot = { IsNormal.class, IsDevelopment.class }) + @Record(ExecutionTime.RUNTIME_INIT) + void registerJolokiaServerShutdownHook( + JolokiaServerBuildItem jolokiaServer, + ShutdownContextBuildItem shutdownContextBuildItem, + JolokiaRecorder recorder) { + recorder.registerJolokiaServerShutdownHook(jolokiaServer.getRuntimeValue(), shutdownContextBuildItem); + } + + @BuildStep(onlyIf = JolokiaManagementEndpointEnabled.class) + @Record(ExecutionTime.RUNTIME_INIT) + void createManagementRoute( + JolokiaServerConfigBuildItem jolokiaServerConfig, + NonApplicationRootPathBuildItem nonApplicationRootPathBuildItem, + BodyHandlerBuildItem bodyHandler, + Capabilities capabilities, + BuildProducer<RouteBuildItem> routes, + JolokiaBuildTimeConfig buildTimeConfig, + JolokiaRecorder recorder) { + + if (capabilities.isPresent(Capability.VERTX_HTTP)) { + String jolokiaEndpointPath = nonApplicationRootPathBuildItem.resolvePath(buildTimeConfig.path()); + routes.produce(nonApplicationRootPathBuildItem.routeBuilder() + .management() + .routeFunction(buildTimeConfig.path() + "/*", recorder.route(bodyHandler.getHandler())) + .handler(recorder.getHandler(jolokiaServerConfig.getRuntimeValue(), jolokiaEndpointPath)) + .build()); + } + } + + @BuildStep(onlyIf = { IsNormal.class, ExposeContainerPortEnabled.class }) + KubernetesPortBuildItem configureJolokiaKubernetesPort() { + return KubernetesPortBuildItem.fromRuntimeConfiguration("jolokia", "quarkus.camel.jolokia.server.port", 8778, true); + } + + @BuildStep(onlyIf = NativeOrNativeSourcesBuild.class) + @Record(value = ExecutionTime.RUNTIME_INIT) + void warnJvmInNative(JvmOnlyRecorder recorder) { + JvmOnlyRecorder.warnJvmInNative(LOG, FEATURE); + recorder.warnJvmInNative(FEATURE); + } + + static final class JolokiaEnabled implements BooleanSupplier { + JolokiaBuildTimeConfig config; + + @Override + public boolean getAsBoolean() { + return config.enabled(); + } + } + + static final class JolokiaManagementEndpointEnabled implements BooleanSupplier { + JolokiaBuildTimeConfig config; + + @Override + public boolean getAsBoolean() { + return config.registerManagementEndpoint(); + } + } + + static final class ExposeContainerPortEnabled implements BooleanSupplier { + JolokiaBuildTimeConfig config; + + @Override + public boolean getAsBoolean() { + return config.kubernetes().exposeContainerPort(); + } + } +} diff --git a/extensions-jvm/jolokia/deployment/src/main/java/org/apache/camel/quarkus/jolokia/deployment/JolokiaServerBuildItem.java b/extensions-jvm/jolokia/deployment/src/main/java/org/apache/camel/quarkus/jolokia/deployment/JolokiaServerBuildItem.java new file mode 100644 index 0000000000..7cba6e98db --- /dev/null +++ b/extensions-jvm/jolokia/deployment/src/main/java/org/apache/camel/quarkus/jolokia/deployment/JolokiaServerBuildItem.java @@ -0,0 +1,33 @@ +/* + * 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.jolokia.deployment; + +import io.quarkus.builder.item.SimpleBuildItem; +import io.quarkus.runtime.RuntimeValue; +import org.jolokia.jvmagent.JolokiaServer; + +final class JolokiaServerBuildItem extends SimpleBuildItem { + private final RuntimeValue<JolokiaServer> server; + + JolokiaServerBuildItem(RuntimeValue<JolokiaServer> server) { + this.server = server; + } + + RuntimeValue<JolokiaServer> getRuntimeValue() { + return server; + } +} diff --git a/extensions-jvm/jolokia/deployment/src/main/java/org/apache/camel/quarkus/jolokia/deployment/JolokiaServerConfigBuildItem.java b/extensions-jvm/jolokia/deployment/src/main/java/org/apache/camel/quarkus/jolokia/deployment/JolokiaServerConfigBuildItem.java new file mode 100644 index 0000000000..2c9ef758ca --- /dev/null +++ b/extensions-jvm/jolokia/deployment/src/main/java/org/apache/camel/quarkus/jolokia/deployment/JolokiaServerConfigBuildItem.java @@ -0,0 +1,33 @@ +/* + * 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.jolokia.deployment; + +import io.quarkus.builder.item.SimpleBuildItem; +import io.quarkus.runtime.RuntimeValue; +import org.jolokia.jvmagent.JolokiaServerConfig; + +final class JolokiaServerConfigBuildItem extends SimpleBuildItem { + private final RuntimeValue<JolokiaServerConfig> config; + + JolokiaServerConfigBuildItem(RuntimeValue<JolokiaServerConfig> config) { + this.config = config; + } + + public RuntimeValue<JolokiaServerConfig> getRuntimeValue() { + return config; + } +} diff --git a/extensions-jvm/jolokia/deployment/src/test/java/org/apache/camel/quarkus/jolokia/JolokiaCustomContextPathTest.java b/extensions-jvm/jolokia/deployment/src/test/java/org/apache/camel/quarkus/jolokia/JolokiaCustomContextPathTest.java new file mode 100644 index 0000000000..6965c44c95 --- /dev/null +++ b/extensions-jvm/jolokia/deployment/src/test/java/org/apache/camel/quarkus/jolokia/JolokiaCustomContextPathTest.java @@ -0,0 +1,44 @@ +/* + * 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.jolokia; + +import io.quarkus.test.QuarkusUnitTest; +import io.restassured.RestAssured; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +class JolokiaCustomContextPathTest { + @RegisterExtension + static final QuarkusUnitTest CONFIG = new QuarkusUnitTest() + .withEmptyApplication() + .overrideConfigKey("quarkus.camel.jolokia.path", "test"); + + @Test + void contextPathAccessible() { + RestAssured.port = 8778; + RestAssured.get("/test/") + .then() + .statusCode(200); + } + + @Test + void managementEndpointContextPathAccessible() { + RestAssured.get("/q/test") + .then() + .statusCode(200); + } +} diff --git a/extensions-jvm/jolokia/deployment/src/test/java/org/apache/camel/quarkus/jolokia/JolokiaDisabledTest.java b/extensions-jvm/jolokia/deployment/src/test/java/org/apache/camel/quarkus/jolokia/JolokiaDisabledTest.java new file mode 100644 index 0000000000..97c71b5ab1 --- /dev/null +++ b/extensions-jvm/jolokia/deployment/src/test/java/org/apache/camel/quarkus/jolokia/JolokiaDisabledTest.java @@ -0,0 +1,38 @@ +/* + * 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.jolokia; + +import java.net.ConnectException; +import java.net.URI; + +import io.quarkus.test.QuarkusUnitTest; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +class JolokiaDisabledTest { + @RegisterExtension + static final QuarkusUnitTest CONFIG = new QuarkusUnitTest() + .withEmptyApplication() + .overrideConfigKey("quarkus.camel.jolokia.enabled", "false"); + + @Test + void jolokiaUnreachable() { + URI uri = URI.create("http://localhost:8778/jolokia"); + Assertions.assertThrows(ConnectException.class, () -> uri.toURL().openConnection().connect()); + } +} diff --git a/extensions-jvm/jolokia/deployment/src/test/java/org/apache/camel/quarkus/jolokia/JolokiaEnabledTest.java b/extensions-jvm/jolokia/deployment/src/test/java/org/apache/camel/quarkus/jolokia/JolokiaEnabledTest.java new file mode 100644 index 0000000000..cdfe1122de --- /dev/null +++ b/extensions-jvm/jolokia/deployment/src/test/java/org/apache/camel/quarkus/jolokia/JolokiaEnabledTest.java @@ -0,0 +1,90 @@ +/* + * 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.jolokia; + +import io.quarkus.test.QuarkusUnitTest; +import io.restassured.RestAssured; +import io.restassured.http.ContentType; +import io.restassured.response.Response; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import org.apache.camel.ConsumerTemplate; +import org.apache.camel.builder.RouteBuilder; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.greaterThan; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +class JolokiaEnabledTest { + @RegisterExtension + static final QuarkusUnitTest CONFIG = new QuarkusUnitTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) + .addClass(Routes.class)); + + @Inject + ConsumerTemplate consumerTemplate; + + @Test + void getCamelContextMBean() { + RestAssured.port = 8778; + RestAssured.get("/jolokia/read/org.apache.camel:context=camel-1,type=context,name=\"camel-1\"") + .then() + .statusCode(200) + .body( + "value.UptimeMillis", greaterThan(0), + "value.TotalRoutes", equalTo(1)); + } + + @Test + void sendMessageToRoute() { + String jolokiaPayload = "{\"type\":\"exec\",\"mbean\":\"org.apache.camel:context=camel-1,type=context,name=\\\"camel-1\\\"\",\"operation\":\"sendStringBody(java.lang.String, java.lang.String)\",\"arguments\":[\"direct://start\",\"Hello World\"]}"; + Response response = RestAssured.given() + .contentType(ContentType.JSON) + .body(jolokiaPayload) + // Test the Quarkus management endpoint returns a redirect to the Jolokia server + .post("/q/jolokia/"); + + if (response.statusCode() == 301) { + String newUrl = response.getHeader("Location"); + + RestAssured.given() + .body(jolokiaPayload) + .post(newUrl) + .then() + .statusCode(200) + .body("status", equalTo(200)); + } else { + fail("Unexpected status code: " + response.statusCode()); + } + + String message = consumerTemplate.receiveBody("seda:end", 10000, String.class); + assertEquals("Hello World", message); + } + + @ApplicationScoped + public static class Routes extends RouteBuilder { + @Override + public void configure() throws Exception { + from("direct:start").to("seda:end"); + } + } +} diff --git a/extensions-jvm/jolokia/deployment/src/test/java/org/apache/camel/quarkus/jolokia/JolokiaManagementEndpointDisabledTest.java b/extensions-jvm/jolokia/deployment/src/test/java/org/apache/camel/quarkus/jolokia/JolokiaManagementEndpointDisabledTest.java new file mode 100644 index 0000000000..983354f211 --- /dev/null +++ b/extensions-jvm/jolokia/deployment/src/test/java/org/apache/camel/quarkus/jolokia/JolokiaManagementEndpointDisabledTest.java @@ -0,0 +1,36 @@ +/* + * 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.jolokia; + +import io.quarkus.test.QuarkusUnitTest; +import io.restassured.RestAssured; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +class JolokiaManagementEndpointDisabledTest { + @RegisterExtension + static final QuarkusUnitTest CONFIG = new QuarkusUnitTest() + .withEmptyApplication() + .overrideConfigKey("quarkus.camel.jolokia.register-management-endpoint", "false"); + + @Test + void managementEndpointUnreachable() { + RestAssured.get("/q/jolokia") + .then() + .statusCode(404); + } +} diff --git a/extensions-jvm/jolokia/pom.xml b/extensions-jvm/jolokia/pom.xml new file mode 100644 index 0000000000..c553672392 --- /dev/null +++ b/extensions-jvm/jolokia/pom.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + 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. + +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.apache.camel.quarkus</groupId> + <artifactId>camel-quarkus-extensions-jvm</artifactId> + <version>3.19.0-SNAPSHOT</version> + <relativePath>../pom.xml</relativePath> + </parent> + + <artifactId>camel-quarkus-jolokia-parent</artifactId> + <name>Camel Quarkus :: Jolokia</name> + <packaging>pom</packaging> + + <modules> + <module>deployment</module> + <module>runtime</module> + </modules> +</project> diff --git a/extensions-jvm/jolokia/runtime/pom.xml b/extensions-jvm/jolokia/runtime/pom.xml new file mode 100644 index 0000000000..3c8d75a105 --- /dev/null +++ b/extensions-jvm/jolokia/runtime/pom.xml @@ -0,0 +1,88 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + 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. + +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.apache.camel.quarkus</groupId> + <artifactId>camel-quarkus-jolokia-parent</artifactId> + <version>3.19.0-SNAPSHOT</version> + <relativePath>../pom.xml</relativePath> + </parent> + + <artifactId>camel-quarkus-jolokia</artifactId> + <name>Camel Quarkus :: Jolokia :: Runtime</name> + <description>Expose runtime metrics and management operations via JMX with Jolokia</description> + + <properties> + <camel.quarkus.jvmSince>3.19.0</camel.quarkus.jvmSince> + </properties> + + <dependencies> + <dependency> + <groupId>io.quarkus</groupId> + <artifactId>quarkus-vertx-http</artifactId> + </dependency> + <dependency> + <groupId>org.apache.camel.quarkus</groupId> + <artifactId>camel-quarkus-management</artifactId> + </dependency> + <dependency> + <groupId>org.jolokia</groupId> + <artifactId>jolokia-agent-jvm</artifactId> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>io.quarkus</groupId> + <artifactId>quarkus-extension-maven-plugin</artifactId> + <configuration> + <parentFirstArtifacts> + <parentFirstArtifact>org.jolokia:jolokia-agent-jvm</parentFirstArtifact> + <parentFirstArtifact>org.jolokia:jolokia-json</parentFirstArtifact> + <parentFirstArtifact>org.jolokia:jolokia-server-core</parentFirstArtifact> + <parentFirstArtifact>org.jolokia:jolokia-server-detector</parentFirstArtifact> + <parentFirstArtifact>org.jolokia:jolokia-service-discovery</parentFirstArtifact> + <parentFirstArtifact>org.jolokia:jolokia-service-history</parentFirstArtifact> + <parentFirstArtifact>org.jolokia:jolokia-service-notif-pull</parentFirstArtifact> + <parentFirstArtifact>org.jolokia:jolokia-service-notif-sse</parentFirstArtifact> + <parentFirstArtifact>org.jolokia:jolokia-service-jmx</parentFirstArtifact> + <parentFirstArtifact>org.jolokia:jolokia-service-jsr160</parentFirstArtifact> + <parentFirstArtifact>org.jolokia:jolokia-service-serializer</parentFirstArtifact> + </parentFirstArtifacts> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <configuration> + <annotationProcessorPaths> + <path> + <groupId>io.quarkus</groupId> + <artifactId>quarkus-extension-processor</artifactId> + <version>${quarkus.version}</version> + </path> + </annotationProcessorPaths> + </configuration> + </plugin> + </plugins> + </build> +</project> diff --git a/extensions-jvm/jolokia/runtime/src/main/doc/usage.adoc b/extensions-jvm/jolokia/runtime/src/main/doc/usage.adoc new file mode 100644 index 0000000000..4da0826ee9 --- /dev/null +++ b/extensions-jvm/jolokia/runtime/src/main/doc/usage.adoc @@ -0,0 +1,106 @@ +This extension adds https://jolokia.org/[Jolokia] support to your application. + +=== Jolokia HTTP endpoints + +In prod mode, Jolokia is accessible at the following URLs. + +* http://0.0.0.0:8778/jolokia/ +* http://0.0.0.0:8080/q/jolokia + +In dev and test modes Jolokia is bound only to `localhost`. + +To disable exposing Jolokia via the Quarkus management interface at `/q/jolokia`, add the following configuration to `application.properties`. + +[source] +---- +quarkus.camel.jolokia.register-management-endpoint=false +---- + +If you want to disable Jolokia entirely, then add the following configuration to `application.properties`. + +[source] +---- +quarkus.camel.jolokia.enabled=false +---- + +=== Jolokia configuration + +Any of the https://jolokia.org/reference/html/manual/agents.html[Jolokia configuration options] can be configured via the `quarkus.camel.jolokia.additional-properties.<jolokia-property-name>` option. +Where `<jolokia-property-name>` is the name of the Jolokia configuration option you want to set. + +For example, the following configuration added to `application.properties` enables Jolokia debugging and sets the max depth for traversing bean properties. + +[source] +---- +quarkus.camel.jolokia.additional-properties.debug=true +quarkus.camel.jolokia.additional-properties.maxDepth=10 +---- + +=== Jolokia restrictor + +By default, a Jolokia restrictor is automatically registered that exposes access to only a specific set of MBean domains. + +* `org.apache.camel` +* `java.lang` +* `java.nio` + +If this is too restrictive, then you can either disable the default restrictor, or create your own custom restrictor. + +==== Disable the default restrictor + +The following configuration added to `application.properties` disables the default restrictor. + +[source] +---- +quarkus.camel.jolokia.register-camel-restrictor=false +---- + +==== Create a custom restrictor + +You can create your own restrictor class and register it with Jolokia. + +[source,java] +---- +public class CustomRestrictor extends AllowAllRestrictor { + // Override methods to apply custom restrictions +} +---- + +Register the restrictor with Jolokia by adding the following configuration to `application.properties`. + +[source] +---- +quarkus.camel.jolokia.additional-properties.restrictorClass=org.acme.CustomRestrictor +---- + +=== Kubernetes & OpenShift support + +==== Generated Kubernetes manifests + +If the `quarkus-kubernetes` or `quarkus-openshift` extensions are present, a container port named `jolokia` will be added automatically to the pod configuration within the generated Kubernetes manifest resources. + +This can be disabled by adding the following configuration to `application.properties`. + +[source] +---- +quarkus.camel.jolokia.kubernetes.expose-container-port=false +---- + +==== Automatic enablement of SSL client authentication + +If the application detects that it is running on Kubernetes or OpenShift, then Jolokia is automatically configured for SSL client authentication. +This is useful if you use tools like https://hawt.io/[Hawtio] to discover and connect to your running application pod. + +This functionality can be disabled by adding the following configuration to `application.properties`. + +[source] +---- +quarkus.camel.jolokia.kubernetes.client-authentication-enabled=false +---- + +Note that if you choose to use https://github.com/hawtio/hawtio-online[hawtio-online] to connect to your running application, then you must configure the Jolokia client principal. + +[source] +---- +quarkus.camel.jolokia.kubernetes.client-principal="cn=hawtio-online.hawtio.svc" +---- diff --git a/extensions-jvm/jolokia/runtime/src/main/java/org/apache/camel/quarkus/jolokia/CamelQuarkusJolokiaLogHandler.java b/extensions-jvm/jolokia/runtime/src/main/java/org/apache/camel/quarkus/jolokia/CamelQuarkusJolokiaLogHandler.java new file mode 100644 index 0000000000..3b66e757ea --- /dev/null +++ b/extensions-jvm/jolokia/runtime/src/main/java/org/apache/camel/quarkus/jolokia/CamelQuarkusJolokiaLogHandler.java @@ -0,0 +1,44 @@ +/* + * 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.jolokia; + +import org.jboss.logging.Logger; +import org.jolokia.server.core.service.api.LogHandler; + +final class CamelQuarkusJolokiaLogHandler implements LogHandler { + private static final Logger LOG = Logger.getLogger(CamelQuarkusJolokiaLogHandler.class); + + @Override + public void debug(String s) { + LOG.debug(s); + } + + @Override + public void info(String s) { + LOG.info(s); + } + + @Override + public void error(String s, Throwable throwable) { + LOG.error(s); + } + + @Override + public boolean isDebug() { + return LOG.isDebugEnabled(); + } +} diff --git a/extensions-jvm/jolokia/runtime/src/main/java/org/apache/camel/quarkus/jolokia/CamelQuarkusJolokiaServer.java b/extensions-jvm/jolokia/runtime/src/main/java/org/apache/camel/quarkus/jolokia/CamelQuarkusJolokiaServer.java new file mode 100644 index 0000000000..90ac1c6c2a --- /dev/null +++ b/extensions-jvm/jolokia/runtime/src/main/java/org/apache/camel/quarkus/jolokia/CamelQuarkusJolokiaServer.java @@ -0,0 +1,35 @@ +/* + * 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.jolokia; + +import org.jolokia.jvmagent.JolokiaServer; + +public class CamelQuarkusJolokiaServer { + private final JolokiaServer server; + + CamelQuarkusJolokiaServer(JolokiaServer server) { + this.server = server; + } + + public synchronized void start() { + this.server.start(); + } + + public synchronized void stop() { + this.server.stop(); + } +} diff --git a/extensions-jvm/jolokia/runtime/src/main/java/org/apache/camel/quarkus/jolokia/JolokiaRecorder.java b/extensions-jvm/jolokia/runtime/src/main/java/org/apache/camel/quarkus/jolokia/JolokiaRecorder.java new file mode 100644 index 0000000000..da1d9be146 --- /dev/null +++ b/extensions-jvm/jolokia/runtime/src/main/java/org/apache/camel/quarkus/jolokia/JolokiaRecorder.java @@ -0,0 +1,179 @@ +/* + * 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.jolokia; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.URI; +import java.net.UnknownHostException; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.function.Consumer; + +import io.quarkus.runtime.LaunchMode; +import io.quarkus.runtime.RuntimeValue; +import io.quarkus.runtime.ShutdownContext; +import io.quarkus.runtime.annotations.Recorder; +import io.vertx.core.Handler; +import io.vertx.ext.web.Route; +import io.vertx.ext.web.RoutingContext; +import org.apache.camel.quarkus.jolokia.config.JolokiaRuntimeConfig; +import org.apache.camel.quarkus.jolokia.config.JolokiaRuntimeConfig.DiscoveryEnabledMode; +import org.apache.camel.quarkus.jolokia.config.JolokiaRuntimeConfig.Kubernetes; +import org.apache.camel.quarkus.jolokia.config.JolokiaRuntimeConfig.Server; +import org.apache.camel.quarkus.jolokia.restrictor.CamelJolokiaRestrictor; +import org.apache.camel.util.CollectionHelper; +import org.apache.camel.util.HostUtils; +import org.apache.camel.util.ObjectHelper; +import org.eclipse.microprofile.config.ConfigProvider; +import org.jboss.logging.Logger; +import org.jolokia.jvmagent.JolokiaServer; +import org.jolokia.jvmagent.JolokiaServerConfig; +import org.jolokia.server.core.config.ConfigKey; + +import static io.smallrye.common.os.Linux.isWSL; + +@Recorder +public class JolokiaRecorder { + private static final String ALL_INTERFACES = "0.0.0.0"; + private static final String LOCALHOST = "localhost"; + private static final Logger LOG = Logger.getLogger(JolokiaRequestRedirectHandler.class); + + public Consumer<Route> route(Handler<RoutingContext> bodyHandler) { + return new Consumer<Route>() { + @Override + public void accept(Route route) { + route.handler(bodyHandler).produces("application/json"); + } + }; + } + + public RuntimeValue<JolokiaServerConfig> createJolokiaServerConfig( + JolokiaRuntimeConfig runtimeConfig, + String endpointPath) { + + Server server = runtimeConfig.server(); + Kubernetes kubernetes = runtimeConfig.kubernetes(); + + // Configure Jolokia HTTP server host, port & context path + String host = runtimeConfig.server().host().orElse(null); + if (ObjectHelper.isEmpty(host)) { + if (LaunchMode.isRemoteDev()) { + host = ALL_INTERFACES; + } else if (LaunchMode.current().isDevOrTest()) { + if (!isWSL()) { + host = LOCALHOST; + } else { + host = ALL_INTERFACES; + } + } else { + host = ALL_INTERFACES; + } + } + + Map<String, String> serverOptions = new HashMap<>(); + serverOptions.put("host", host); + serverOptions.put("port", String.valueOf(server.port())); + serverOptions.put(ConfigKey.AGENT_CONTEXT.getKeyValue(), "/" + endpointPath); + + // Attempt Kubernetes configuration + Optional<String> kubernetesServiceHost = ConfigProvider.getConfig().getOptionalValue("kubernetes.service.host", + String.class); + if (kubernetesServiceHost.isPresent()) { + if (kubernetes.clientAuthenticationEnabled() && kubernetes.serviceCaCert().exists()) { + serverOptions.put(ConfigKey.DISCOVERY_ENABLED.getKeyValue(), "false"); + serverOptions.put("protocol", "https"); + serverOptions.put("useSslClientAuthentication", "true"); + serverOptions.put("extendedClientCheck", "true"); + serverOptions.put("caCert", kubernetes.serviceCaCert().getAbsolutePath()); + kubernetes.clientPrincipal() + .ifPresent(clientPrincipal -> serverOptions.put("clientPrincipal", clientPrincipal)); + } else { + LOG.warnf("Kubernetes service CA certificate %s does not exist", kubernetes.serviceCaCert()); + } + } + + // Merge configuration with any arbitrary values provided via quarkus.camel.jolokia.additional-properties + Map<String, String> combinedOptions = CollectionHelper.mergeMaps(serverOptions, + runtimeConfig.additionalProperties()); + + // Configure CamelJolokiaRestrictor if an existing restrictor is not already provided + if (runtimeConfig.registerCamelRestrictor()) { + combinedOptions.putIfAbsent(ConfigKey.RESTRICTOR_CLASS.getKeyValue(), CamelJolokiaRestrictor.class.getName()); + } + + // Enable discovery based on the provided mode + DiscoveryEnabledMode discoveryMode = server.discoveryEnabledMode(); + if (discoveryMode != DiscoveryEnabledMode.NONE) { + if ((discoveryMode == DiscoveryEnabledMode.ALL) + || (discoveryMode == DiscoveryEnabledMode.DEV_TEST && LaunchMode.current().isDevOrTest())) { + combinedOptions.putIfAbsent(ConfigKey.DISCOVERY_ENABLED.getKeyValue(), "true"); + } + } + + return new RuntimeValue<>(new JolokiaServerConfig(combinedOptions)); + } + + public RuntimeValue<JolokiaServer> createJolokiaServer(RuntimeValue<JolokiaServerConfig> serverConfig) { + try { + CamelQuarkusJolokiaAgent agent = new CamelQuarkusJolokiaAgent(serverConfig.getValue()); + return new RuntimeValue<>(agent); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public void startJolokiaServer(RuntimeValue<JolokiaServer> jolokiaServer, JolokiaRuntimeConfig config) { + if (config.server().autoStart()) { + jolokiaServer.getValue().start(); + } + } + + public void registerJolokiaServerShutdownHook(RuntimeValue<JolokiaServer> jolokiaServer, ShutdownContext shutdownContext) { + shutdownContext.addShutdownTask(() -> jolokiaServer.getValue().stop()); + } + + public RuntimeValue<CamelQuarkusJolokiaServer> createJolokiaServerBean(RuntimeValue<JolokiaServer> jolokiaServer) { + return new RuntimeValue<>(new CamelQuarkusJolokiaServer(jolokiaServer.getValue())); + } + + static final class CamelQuarkusJolokiaAgent extends JolokiaServer { + CamelQuarkusJolokiaAgent(JolokiaServerConfig config) throws IOException { + super(config, new CamelQuarkusJolokiaLogHandler()); + } + } + + public Handler<RoutingContext> getHandler(RuntimeValue<JolokiaServerConfig> config, String jolokiaEndpointPath) { + JolokiaServerConfig serverConfig = config.getValue(); + String host = resolveHost(serverConfig.getAddress()); + URI uri = URI.create("%s://%s:%d%s".formatted(serverConfig.getProtocol(), host, serverConfig.getPort(), + serverConfig.getContextPath())); + return new JolokiaRequestRedirectHandler(uri.normalize(), jolokiaEndpointPath); + } + + static String resolveHost(InetAddress address) { + if (address == null) { + try { + return HostUtils.getLocalHostName(); + } catch (UnknownHostException e) { + throw new IllegalStateException("Unable to determine the Jolokia host", e); + } + } + return address.getHostName(); + } +} diff --git a/extensions-jvm/jolokia/runtime/src/main/java/org/apache/camel/quarkus/jolokia/JolokiaRequestRedirectHandler.java b/extensions-jvm/jolokia/runtime/src/main/java/org/apache/camel/quarkus/jolokia/JolokiaRequestRedirectHandler.java new file mode 100644 index 0000000000..69b1cc496b --- /dev/null +++ b/extensions-jvm/jolokia/runtime/src/main/java/org/apache/camel/quarkus/jolokia/JolokiaRequestRedirectHandler.java @@ -0,0 +1,44 @@ +/* + * 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.jolokia; + +import java.net.URI; + +import io.vertx.core.Handler; +import io.vertx.ext.web.RoutingContext; + +/** + * Vert.x route to redirect from /q/jolokia to the Jolokia embedded HTTP server endpoint + */ +final class JolokiaRequestRedirectHandler implements Handler<RoutingContext> { + private final String jolokiaAgentBaseUrl; + private final String jolokiaManagementEndpointPath; + + public JolokiaRequestRedirectHandler(URI jolokiaAgentBaseUrl, String jolokiaManagementEndpointPath) { + this.jolokiaManagementEndpointPath = jolokiaManagementEndpointPath; + this.jolokiaAgentBaseUrl = jolokiaAgentBaseUrl.toString(); + } + + @Override + public void handle(RoutingContext routingContext) { + String uri = routingContext.request().uri(); + routingContext.response() + .setStatusCode(301) + .putHeader("Location", jolokiaAgentBaseUrl + uri.replace(jolokiaManagementEndpointPath, "")) + .end(); + } +} diff --git a/extensions-jvm/jolokia/runtime/src/main/java/org/apache/camel/quarkus/jolokia/config/JolokiaBuildTimeConfig.java b/extensions-jvm/jolokia/runtime/src/main/java/org/apache/camel/quarkus/jolokia/config/JolokiaBuildTimeConfig.java new file mode 100644 index 0000000000..afa7dcf180 --- /dev/null +++ b/extensions-jvm/jolokia/runtime/src/main/java/org/apache/camel/quarkus/jolokia/config/JolokiaBuildTimeConfig.java @@ -0,0 +1,63 @@ +/* + * 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.jolokia.config; + +import io.quarkus.runtime.annotations.ConfigPhase; +import io.quarkus.runtime.annotations.ConfigRoot; +import io.smallrye.config.ConfigMapping; +import io.smallrye.config.WithDefault; + +@ConfigMapping(prefix = "quarkus.camel.jolokia") +@ConfigRoot(phase = ConfigPhase.BUILD_AND_RUN_TIME_FIXED) +public interface JolokiaBuildTimeConfig { + /** + * Enables Jolokia support. + */ + @WithDefault("true") + boolean enabled(); + + /** + * The context path that the Jolokia agent is deployed under. + */ + @WithDefault("jolokia") + String path(); + + /** + * Whether to register a Quarkus management endpoint for Jolokia (default /q/jolokia). + * When enabled this activates a management endpoint which will be accessible on a path relative to + * ${quarkus.http.non-application-root-path}/${quarkus.camel.jolokia.server.path}. + * If the management interface is enabled, the value will be resolved as a path relative to + * ${quarkus.management.root-path}/${quarkus.camel.jolokia.server.path}. Note that for this feature to work you must + * have quarkus-vertx-http on the application classpath. + */ + @WithDefault("true") + boolean registerManagementEndpoint(); + + /** + * Jolokia Kubernetes build time configuration. + */ + Kubernetes kubernetes(); + + interface Kubernetes { + /** + * When {@code true} and the quarkus-kubernetes extension is present, a container port named jolokia will + * be added to the generated Kubernetes manifests within the container spec ports definition. + */ + @WithDefault("true") + boolean exposeContainerPort(); + } +} diff --git a/extensions-jvm/jolokia/runtime/src/main/java/org/apache/camel/quarkus/jolokia/config/JolokiaRuntimeConfig.java b/extensions-jvm/jolokia/runtime/src/main/java/org/apache/camel/quarkus/jolokia/config/JolokiaRuntimeConfig.java new file mode 100644 index 0000000000..d250d9e89e --- /dev/null +++ b/extensions-jvm/jolokia/runtime/src/main/java/org/apache/camel/quarkus/jolokia/config/JolokiaRuntimeConfig.java @@ -0,0 +1,118 @@ +/* + * 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.jolokia.config; + +import java.io.File; +import java.util.Map; +import java.util.Optional; + +import io.quarkus.runtime.annotations.ConfigPhase; +import io.quarkus.runtime.annotations.ConfigRoot; +import io.smallrye.config.ConfigMapping; +import io.smallrye.config.WithDefault; + +@ConfigMapping(prefix = "quarkus.camel.jolokia") +@ConfigRoot(phase = ConfigPhase.RUN_TIME) +public interface JolokiaRuntimeConfig { + /** + * Jolokia agent HTTP server configuration. + */ + Server server(); + + /** + * Kubernetes runtime configuration. + */ + Kubernetes kubernetes(); + + /** + * Arbitrary Jolokia configuration options. These are described at the + * <a href="https://jolokia.org/reference/html/manual/agents.html">Jolokia documentation</a>. + * Options can be configured like {@code quarkus.camel.jolokia.additional-properties."debug"=true}. + */ + Map<String, String> additionalProperties(); + + /** + * When {@code true}, a Jolokia restrictor is registered that limits MBean read, write and operation execution to the + * following MBean domains. + * <ul> + * <li>org.apache.camel</li> + * <li>java.lang</li> + * <li>java.nio</li> + * </ul> + * Note that this option has no effect if quarkus.camel.jolokia.additional-properties."restrictorClass" is set. + */ + @WithDefault("true") + boolean registerCamelRestrictor(); + + interface Server { + /** + * Whether the Jolokia agent HTTP server should be started automatically. + * When set to {@code false}, it is the user responsibility to start the server. + * This can be done via {@code @Inject CamelQuarkusJolokiaServer} and then invoking the start() method. + */ + @WithDefault("true") + boolean autoStart(); + + /** + * The host address to which the Jolokia agent HTTP server should bind to. + * When unspecified, the default is localhost for dev and test mode. + * In prod mode the default is to bind to all interfaces at 0.0.0.0. + */ + Optional<String> host(); + + /** + * The port on which the Jolokia agent HTTP server should listen on. + */ + @WithDefault("8778") + int port(); + + /** + * The mode in which Jolokia agent discovery is enabled. The default {@code dev-test}, enables discovery only in dev and + * test modes. + * A value of {@code all} enables agent discovery in dev, test and prod modes. Setting the value to {@code none} will + * disable agent discovery in all modes. + */ + @WithDefault("DEV_TEST") + DiscoveryEnabledMode discoveryEnabledMode(); + } + + enum DiscoveryEnabledMode { + ALL, + DEV_TEST, + NONE, + } + + interface Kubernetes { + /** + * Whether to enable Jolokia SSL client authentication in Kubernetes environments. + * Useful for tools such as hawtio to be able to connect with your application. + */ + @WithDefault("true") + boolean clientAuthenticationEnabled(); + + /** + * Absolute path of the CA certificate Jolokia should use for SSL client authentication. + */ + @WithDefault("/var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt") + File serviceCaCert(); + + /** + * The principal which must be given in a client certificate to allow access to Jolokia. + */ + Optional<String> clientPrincipal(); + } +} diff --git a/extensions-jvm/jolokia/runtime/src/main/java/org/apache/camel/quarkus/jolokia/restrictor/CamelJolokiaRestrictor.java b/extensions-jvm/jolokia/runtime/src/main/java/org/apache/camel/quarkus/jolokia/restrictor/CamelJolokiaRestrictor.java new file mode 100644 index 0000000000..43b5016792 --- /dev/null +++ b/extensions-jvm/jolokia/runtime/src/main/java/org/apache/camel/quarkus/jolokia/restrictor/CamelJolokiaRestrictor.java @@ -0,0 +1,51 @@ +/* + * 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.jolokia.restrictor; + +import java.util.List; + +import javax.management.ObjectName; + +import org.jolokia.server.core.restrictor.AllowAllRestrictor; + +public final class CamelJolokiaRestrictor extends AllowAllRestrictor { + private static final List<String> ALLOWED_DOMAINS = List.of("org.apache.camel", "java.lang", "java.nio"); + + @Override + public boolean isAttributeReadAllowed(ObjectName objectName, String attribute) { + return isAllowedDomain(objectName); + } + + @Override + public boolean isAttributeWriteAllowed(ObjectName objectName, String attribute) { + return isAllowedDomain(objectName); + } + + @Override + public boolean isOperationAllowed(ObjectName objectName, String operation) { + return isAllowedDomain(objectName); + } + + @Override + public boolean isObjectNameHidden(ObjectName objectName) { + return !isAllowedDomain(objectName); + } + + private boolean isAllowedDomain(ObjectName objectName) { + return ALLOWED_DOMAINS.contains(objectName.getDomain()); + } +} diff --git a/extensions-jvm/jolokia/runtime/src/main/resources/META-INF/quarkus-extension.yaml b/extensions-jvm/jolokia/runtime/src/main/resources/META-INF/quarkus-extension.yaml new file mode 100644 index 0000000000..93c5bd3ca6 --- /dev/null +++ b/extensions-jvm/jolokia/runtime/src/main/resources/META-INF/quarkus-extension.yaml @@ -0,0 +1,34 @@ +# +# 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. +# + +# This is a generated file. Do not edit directly! +# To re-generate, run the following command from the top level directory: +# +# mvn -N cq:update-quarkus-metadata +# +--- +name: "Camel Jolokia" +description: "Expose runtime metrics and management operations via JMX with Jolokia" +metadata: + icon-url: "https://raw.githubusercontent.com/apache/camel-website/main/antora-ui-camel/src/img/logo-d.svg" + sponsor: "Apache Software Foundation" + unlisted: true + guide: "https://camel.apache.org/camel-quarkus/latest/reference/extensions/jolokia.html" + categories: + - "integration" + status: + - "preview" diff --git a/extensions-jvm/pom.xml b/extensions-jvm/pom.xml index 6e44f46d8c..4d1631bc80 100644 --- a/extensions-jvm/pom.xml +++ b/extensions-jvm/pom.xml @@ -69,6 +69,7 @@ <module>jcr</module> <module>jgroups</module> <module>jgroups-raft</module> + <module>jolokia</module> <module>jooq</module> <module>json-patch</module> <module>jsonapi</module> diff --git a/integration-tests-jvm/jolokia/pom.xml b/integration-tests-jvm/jolokia/pom.xml new file mode 100644 index 0000000000..ce7645dd69 --- /dev/null +++ b/integration-tests-jvm/jolokia/pom.xml @@ -0,0 +1,132 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + 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. + +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.apache.camel.quarkus</groupId> + <artifactId>camel-quarkus-build-parent-it</artifactId> + <version>3.19.0-SNAPSHOT</version> + <relativePath>../../poms/build-parent-it/pom.xml</relativePath> + </parent> + + <artifactId>camel-quarkus-integration-test-jolokia</artifactId> + <name>Camel Quarkus :: Integration Tests :: Jolokia</name> + <description>Integration tests for Camel Quarkus Jolokia extension</description> + + <dependencies> + <dependency> + <groupId>org.apache.camel.quarkus</groupId> + <artifactId>camel-quarkus-jolokia</artifactId> + </dependency> + <dependency> + <groupId>org.apache.camel.quarkus</groupId> + <artifactId>camel-quarkus-direct</artifactId> + </dependency> + <dependency> + <groupId>org.apache.camel.quarkus</groupId> + <artifactId>camel-quarkus-seda</artifactId> + </dependency> + <dependency> + <groupId>io.quarkus</groupId> + <artifactId>quarkus-resteasy</artifactId> + </dependency> + + <!-- test dependencies --> + <dependency> + <groupId>io.quarkus</groupId> + <artifactId>quarkus-junit5</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>io.rest-assured</groupId> + <artifactId>rest-assured</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.awaitility</groupId> + <artifactId>awaitility</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.camel.quarkus</groupId> + <artifactId>camel-quarkus-integration-test-support</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>io.smallrye.certs</groupId> + <artifactId>smallrye-certificate-generator-junit5</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + + <profiles> + <profile> + <id>virtualDependencies</id> + <activation> + <property> + <name>!noVirtualDependencies</name> + </property> + </activation> + <dependencies> + <!-- The following dependencies guarantee that this module is built after them. You can update them by running `mvn process-resources -Pformat -N` from the source tree root directory --> + <dependency> + <groupId>org.apache.camel.quarkus</groupId> + <artifactId>camel-quarkus-direct-deployment</artifactId> + <version>${project.version}</version> + <type>pom</type> + <scope>test</scope> + <exclusions> + <exclusion> + <groupId>*</groupId> + <artifactId>*</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>org.apache.camel.quarkus</groupId> + <artifactId>camel-quarkus-jolokia-deployment</artifactId> + <version>${project.version}</version> + <type>pom</type> + <scope>test</scope> + <exclusions> + <exclusion> + <groupId>*</groupId> + <artifactId>*</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>org.apache.camel.quarkus</groupId> + <artifactId>camel-quarkus-seda-deployment</artifactId> + <version>${project.version}</version> + <type>pom</type> + <scope>test</scope> + <exclusions> + <exclusion> + <groupId>*</groupId> + <artifactId>*</artifactId> + </exclusion> + </exclusions> + </dependency> + </dependencies> + </profile> + </profiles> + +</project> diff --git a/integration-tests-jvm/jolokia/src/main/java/org/apache/camel/quarkus/component/jolokia/it/JolokiaResource.java b/integration-tests-jvm/jolokia/src/main/java/org/apache/camel/quarkus/component/jolokia/it/JolokiaResource.java new file mode 100644 index 0000000000..f97be0810c --- /dev/null +++ b/integration-tests-jvm/jolokia/src/main/java/org/apache/camel/quarkus/component/jolokia/it/JolokiaResource.java @@ -0,0 +1,56 @@ +/* + * 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.component.jolokia.it; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import org.apache.camel.ConsumerTemplate; +import org.apache.camel.quarkus.jolokia.CamelQuarkusJolokiaServer; + +@Path("/jolokia") +@ApplicationScoped +public class JolokiaResource { + @Inject + ConsumerTemplate consumerTemplate; + + @Inject + CamelQuarkusJolokiaServer server; + + @Path("/message/get") + @Produces(MediaType.TEXT_PLAIN) + @GET + public String getMessage() { + return consumerTemplate.receiveBody("seda:end", 10000, String.class); + } + + @Path("/start") + @POST + public void start() { + server.start(); + } + + @Path("/stop") + @POST + public void stop() { + server.stop(); + } +} diff --git a/integration-tests-jvm/jolokia/src/main/java/org/apache/camel/quarkus/component/jolokia/it/JolokiaRoutes.java b/integration-tests-jvm/jolokia/src/main/java/org/apache/camel/quarkus/component/jolokia/it/JolokiaRoutes.java new file mode 100644 index 0000000000..439d1155d4 --- /dev/null +++ b/integration-tests-jvm/jolokia/src/main/java/org/apache/camel/quarkus/component/jolokia/it/JolokiaRoutes.java @@ -0,0 +1,27 @@ +/* + * 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.component.jolokia.it; + +import org.apache.camel.builder.RouteBuilder; + +public class JolokiaRoutes extends RouteBuilder { + @Override + public void configure() throws Exception { + from("direct:start") + .to("seda:end"); + } +} diff --git a/integration-tests-jvm/jolokia/src/test/java/org/apache/camel/quarkus/component/jolokia/it/JolokiaAdditionalPropertiesTest.java b/integration-tests-jvm/jolokia/src/test/java/org/apache/camel/quarkus/component/jolokia/it/JolokiaAdditionalPropertiesTest.java new file mode 100644 index 0000000000..03d45d578c --- /dev/null +++ b/integration-tests-jvm/jolokia/src/test/java/org/apache/camel/quarkus/component/jolokia/it/JolokiaAdditionalPropertiesTest.java @@ -0,0 +1,53 @@ +/* + * 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.component.jolokia.it; + +import java.util.Map; + +import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.junit.QuarkusTestProfile; +import io.quarkus.test.junit.TestProfile; +import io.restassured.RestAssured; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.Matchers.equalTo; + +@TestProfile(JolokiaAdditionalPropertiesTest.JolokiaAdditionalPropertiesProfile.class) +@QuarkusTest +class JolokiaAdditionalPropertiesTest { + @BeforeEach + public void beforeEach() { + RestAssured.port = 8778; + } + + @Test + void additionalProperties() { + RestAssured.given() + .get("/jolokia/") + .then() + .statusCode(200) + .body("value.config.maxDepth", equalTo("10")); + } + + public static final class JolokiaAdditionalPropertiesProfile implements QuarkusTestProfile { + @Override + public Map<String, String> getConfigOverrides() { + return Map.of("quarkus.camel.jolokia.additional-properties.maxDepth", "10"); + } + } +} diff --git a/integration-tests-jvm/jolokia/src/test/java/org/apache/camel/quarkus/component/jolokia/it/JolokiaCustomHostPortTest.java b/integration-tests-jvm/jolokia/src/test/java/org/apache/camel/quarkus/component/jolokia/it/JolokiaCustomHostPortTest.java new file mode 100644 index 0000000000..2c24213142 --- /dev/null +++ b/integration-tests-jvm/jolokia/src/test/java/org/apache/camel/quarkus/component/jolokia/it/JolokiaCustomHostPortTest.java @@ -0,0 +1,57 @@ +/* + * 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.component.jolokia.it; + +import java.util.Map; + +import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.junit.QuarkusTestProfile; +import io.quarkus.test.junit.TestProfile; +import io.restassured.RestAssured; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.Matchers.equalTo; + +@TestProfile(JolokiaCustomHostPortTest.JolokiaAdditionalPropertiesProfile.class) +@QuarkusTest +class JolokiaCustomHostPortTest { + private static final int PORT = 8779; + + @BeforeEach + public void beforeEach() { + RestAssured.port = PORT; + } + + @Test + void customHostPort() throws InterruptedException { + RestAssured.given() + .get("/jolokia/") + .then() + .statusCode(200) + .body("status", equalTo(200)); + } + + public static final class JolokiaAdditionalPropertiesProfile implements QuarkusTestProfile { + @Override + public Map<String, String> getConfigOverrides() { + return Map.of( + "quarkus.camel.jolokia.server.host", "0.0.0.0", + "quarkus.camel.jolokia.server.port", String.valueOf(PORT)); + } + } +} diff --git a/integration-tests-jvm/jolokia/src/test/java/org/apache/camel/quarkus/component/jolokia/it/JolokiaDisableAutoStartTest.java b/integration-tests-jvm/jolokia/src/test/java/org/apache/camel/quarkus/component/jolokia/it/JolokiaDisableAutoStartTest.java new file mode 100644 index 0000000000..03cc29d16c --- /dev/null +++ b/integration-tests-jvm/jolokia/src/test/java/org/apache/camel/quarkus/component/jolokia/it/JolokiaDisableAutoStartTest.java @@ -0,0 +1,91 @@ +/* + * 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.component.jolokia.it; + +import java.net.ConnectException; +import java.net.SocketTimeoutException; +import java.time.Duration; +import java.util.Map; + +import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.junit.QuarkusTestProfile; +import io.quarkus.test.junit.TestProfile; +import io.restassured.RestAssured; +import io.restassured.config.HttpClientConfig; +import org.awaitility.Awaitility; +import org.eclipse.microprofile.config.ConfigProvider; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.Matchers.equalTo; +import static org.junit.jupiter.api.Assertions.assertThrows; + +@TestProfile(JolokiaDisableAutoStartTest.JolokiaAdditionalPropertiesProfile.class) +@QuarkusTest +class JolokiaDisableAutoStartTest { + @Test + void autoStartupDisabled() { + // Connecting to Jolokia should not be possible + RestAssured.config = RestAssured.config().httpClient( + HttpClientConfig.httpClientConfig() + .setParam("http.connection.timeout", 1000) + .setParam("http.socket.timeout", 1000)); + + RestAssured.port = 8778; + assertThrows(SocketTimeoutException.class, () -> { + RestAssured.get("/jolokia/") + .then() + .statusCode(204); + }); + + // Manually start Jolokia + RestAssured.port = ConfigProvider.getConfig().getValue("quarkus.http.test-port", Integer.class); + RestAssured.post("/jolokia/start") + .then() + .statusCode(204); + + // Verify a basic request is successful + RestAssured.port = 8778; + Awaitility.await().atMost(Duration.ofSeconds(5)).pollInterval(Duration.ofMillis(100)).untilAsserted(() -> { + RestAssured.given() + .get("/jolokia/") + .then() + .statusCode(200) + .body("status", equalTo(200)); + }); + + // Verify stop. We don't bother putting this in a finally block since a shutdown hook will take care of stopping Jolokia in case of test failure + RestAssured.port = ConfigProvider.getConfig().getValue("quarkus.http.test-port", Integer.class); + RestAssured.post("/jolokia/stop") + .then() + .statusCode(204); + + // Connecting to Jolokia should not be possible + RestAssured.port = 8778; + assertThrows(ConnectException.class, () -> { + RestAssured.get("/jolokia/") + .then() + .statusCode(204); + }); + } + + public static final class JolokiaAdditionalPropertiesProfile implements QuarkusTestProfile { + @Override + public Map<String, String> getConfigOverrides() { + return Map.of("quarkus.camel.jolokia.server.auto-start", "false"); + } + } +} diff --git a/integration-tests-jvm/jolokia/src/test/java/org/apache/camel/quarkus/component/jolokia/it/JolokiaDisableRestrictorTest.java b/integration-tests-jvm/jolokia/src/test/java/org/apache/camel/quarkus/component/jolokia/it/JolokiaDisableRestrictorTest.java new file mode 100644 index 0000000000..94e08bb9da --- /dev/null +++ b/integration-tests-jvm/jolokia/src/test/java/org/apache/camel/quarkus/component/jolokia/it/JolokiaDisableRestrictorTest.java @@ -0,0 +1,53 @@ +/* + * 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.component.jolokia.it; + +import java.util.Map; + +import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.junit.QuarkusTestProfile; +import io.quarkus.test.junit.TestProfile; +import io.restassured.RestAssured; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.Matchers.emptyOrNullString; + +@TestProfile(JolokiaDisableRestrictorTest.JolokiaAdditionalPropertiesProfile.class) +@QuarkusTest +class JolokiaDisableRestrictorTest { + @BeforeEach + public void beforeEach() { + RestAssured.port = 8778; + } + + @Test + void restrictorClassUnconfigured() { + RestAssured.given() + .get("/jolokia/") + .then() + .statusCode(200) + .body("value.config.restrictorClass", emptyOrNullString()); + } + + public static final class JolokiaAdditionalPropertiesProfile implements QuarkusTestProfile { + @Override + public Map<String, String> getConfigOverrides() { + return Map.of("quarkus.camel.jolokia.register-camel-restrictor", "false"); + } + } +} diff --git a/integration-tests-jvm/jolokia/src/test/java/org/apache/camel/quarkus/component/jolokia/it/JolokiaDiscoveryDisabledTest.java b/integration-tests-jvm/jolokia/src/test/java/org/apache/camel/quarkus/component/jolokia/it/JolokiaDiscoveryDisabledTest.java new file mode 100644 index 0000000000..088c4dbc52 --- /dev/null +++ b/integration-tests-jvm/jolokia/src/test/java/org/apache/camel/quarkus/component/jolokia/it/JolokiaDiscoveryDisabledTest.java @@ -0,0 +1,57 @@ +/* + * 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.component.jolokia.it; + +import java.util.Map; + +import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.junit.QuarkusTestProfile; +import io.quarkus.test.junit.TestProfile; +import io.restassured.RestAssured; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.Matchers.equalTo; + +@TestProfile(JolokiaDiscoveryDisabledTest.JolokiaAdditionalPropertiesProfile.class) +@QuarkusTest +class JolokiaDiscoveryDisabledTest { + private static final int PORT = 8778; + + @BeforeEach + public void beforeEach() { + RestAssured.port = PORT; + } + + @Test + void discoveryEnabledFalse() throws InterruptedException { + RestAssured.given() + .get("/jolokia/") + .then() + .statusCode(200) + .body( + "status", equalTo(200), + "value.config.discoveryEnabled", equalTo("false")); + } + + public static final class JolokiaAdditionalPropertiesProfile implements QuarkusTestProfile { + @Override + public Map<String, String> getConfigOverrides() { + return Map.of("quarkus.camel.jolokia.server.discovery-enabled-mode", "none"); + } + } +} diff --git a/integration-tests-jvm/jolokia/src/test/java/org/apache/camel/quarkus/component/jolokia/it/JolokiaKubernetesClientSSLTest.java b/integration-tests-jvm/jolokia/src/test/java/org/apache/camel/quarkus/component/jolokia/it/JolokiaKubernetesClientSSLTest.java new file mode 100644 index 0000000000..9363b9fed5 --- /dev/null +++ b/integration-tests-jvm/jolokia/src/test/java/org/apache/camel/quarkus/component/jolokia/it/JolokiaKubernetesClientSSLTest.java @@ -0,0 +1,93 @@ +/* + * 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.component.jolokia.it; + +import java.io.File; +import java.util.Map; + +import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.junit.QuarkusTestProfile; +import io.quarkus.test.junit.TestProfile; +import io.restassured.RestAssured; +import io.restassured.config.SSLConfig; +import io.smallrye.certs.Format; +import io.smallrye.certs.junit5.Certificate; +import io.smallrye.certs.junit5.Certificates; +import org.apache.http.NoHttpResponseException; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.Matchers.equalTo; +import static org.junit.jupiter.api.Assertions.assertThrows; + +@Certificates(baseDir = "target/certs", certificates = { + @Certificate(name = "kubernetes-service-cert", formats = { Format.PKCS12, + Format.PEM }, password = "2s3cr3t") +}) +@TestProfile(JolokiaKubernetesClientSSLTest.JolokiaAdditionalPropertiesProfile.class) +@QuarkusTest +class JolokiaKubernetesClientSSLTest { + private static final File SERVER_CERT = new File("target/certs/kubernetes-service-cert.crt"); + private static final File SERVER_KEY = new File("target/certs/kubernetes-service-cert.key"); + private static final File CA_CERT = new File("target/certs/kubernetes-service-cert-ca.crt"); + + @BeforeEach + public void beforeEach() { + RestAssured.port = 8778; + } + + @Test + void clientSSLAuthentication() { + // Plain HTTP should be disabled + assertThrows(NoHttpResponseException.class, () -> { + RestAssured.given() + .get("/jolokia/") + .then() + .statusCode(200); + }); + + RestAssured.config = RestAssured.config().with().sslConfig(getSSLConfig()); + RestAssured.given() + .get("https://localhost:8778/jolokia/") + .then() + .statusCode(200) + .body( + "status", equalTo(200), + "value.details.secured", equalTo(true)); + } + + private SSLConfig getSSLConfig() { + return new SSLConfig() + .keystoreType("PKCS12") + .keyStore("target/certs/kubernetes-service-cert-keystore.p12", "2s3cr3t") + .trustStoreType("PKCS12") + .trustStore("target/certs/kubernetes-service-cert-truststore.p12", "2s3cr3t"); + } + + public static final class JolokiaAdditionalPropertiesProfile implements QuarkusTestProfile { + @Override + public Map<String, String> getConfigOverrides() { + return Map.of( + "kubernetes.service.host", "fake-host", + "quarkus.camel.jolokia.kubernetes.service-ca-cert", CA_CERT.getAbsolutePath(), + "quarkus.camel.jolokia.kubernetes.client-principal", "cn=localhost", + "quarkus.camel.jolokia.additional-properties.serverCert", SERVER_CERT.getAbsolutePath(), + "quarkus.camel.jolokia.additional-properties.serverKey", SERVER_KEY.getAbsolutePath(), + "quarkus.camel.jolokia.additional-properties.caCert", CA_CERT.getAbsolutePath()); + } + } +} diff --git a/integration-tests-jvm/jolokia/src/test/java/org/apache/camel/quarkus/component/jolokia/it/JolokiaTest.java b/integration-tests-jvm/jolokia/src/test/java/org/apache/camel/quarkus/component/jolokia/it/JolokiaTest.java new file mode 100644 index 0000000000..6437f36c73 --- /dev/null +++ b/integration-tests-jvm/jolokia/src/test/java/org/apache/camel/quarkus/component/jolokia/it/JolokiaTest.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.component.jolokia.it; + +import io.quarkus.test.junit.QuarkusTest; +import io.restassured.RestAssured; +import io.restassured.http.ContentType; +import org.apache.camel.quarkus.jolokia.restrictor.CamelJolokiaRestrictor; +import org.eclipse.microprofile.config.ConfigProvider; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.Matchers.equalTo; + +@QuarkusTest +class JolokiaTest { + @BeforeEach + public void beforeEach() { + RestAssured.port = 8778; + } + + @Test + void defaultConfiguration() { + RestAssured.given() + .get("/jolokia/") + .then() + .statusCode(200) + .body( + "status", equalTo(200), + "value.config.discoveryEnabled", equalTo("true"), + "value.config.restrictorClass", equalTo(CamelJolokiaRestrictor.class.getName()), + "value.details.url", equalTo("http://127.0.0.1:8778/jolokia/")); + } + + @Test + void sendMessage() { + String jolokiaPayload = "{\"type\":\"exec\",\"mbean\":\"org.apache.camel:context=camel-1,type=context,name=\\\"camel-1\\\"\",\"operation\":\"sendStringBody(java.lang.String, java.lang.String)\",\"arguments\":[\"direct://start\",\"Hello World\"]}"; + RestAssured.given() + .contentType(ContentType.JSON) + .body(jolokiaPayload) + .post("/jolokia/") + .then() + .statusCode(200) + .body("status", equalTo(200)); + + RestAssured.port = ConfigProvider.getConfig().getValue("quarkus.http.test-port", Integer.class); + + RestAssured.get("/jolokia/message/get") + .then() + .statusCode(200) + .body(equalTo("Hello World")); + } +} diff --git a/integration-tests-jvm/pom.xml b/integration-tests-jvm/pom.xml index c322351a65..bb8ba028cd 100644 --- a/integration-tests-jvm/pom.xml +++ b/integration-tests-jvm/pom.xml @@ -68,6 +68,7 @@ <module>jcr</module> <module>jgroups</module> <module>jgroups-raft</module> + <module>jolokia</module> <module>jooq</module> <module>json-patch</module> <module>jsonapi</module> diff --git a/pom.xml b/pom.xml index bee26b1fff..28490c7fc5 100644 --- a/pom.xml +++ b/pom.xml @@ -119,6 +119,7 @@ <jna-platform.version>5.6.0</jna-platform.version><!-- @sync com.azure:azure-identity:${azure-identity.version} dep:net.java.dev.jna:jna-platform --> <jnr-constants.version>0.9.11</jnr-constants.version><!-- Mess in web3j transitive deps --> <jnr-ffi.version>2.2.13</jnr-ffi.version><!-- Mess in web3j transitive deps --> + <jolokia.version>2.2.2</jolokia.version> <jsch.version>0.2.21</jsch.version><!-- @sync io.quarkiverse.jsch:quarkus-jsch-parent:${quarkiverse-jsch.version} prop:jsch.version --> <json-path.version>${json-path-version}</json-path.version> <jedis-client.version>${jedis-client-version}</jedis-client.version> diff --git a/poms/bom/pom.xml b/poms/bom/pom.xml index 63e4afde02..b35597702e 100644 --- a/poms/bom/pom.xml +++ b/poms/bom/pom.xml @@ -4506,6 +4506,16 @@ <artifactId>camel-quarkus-jms-deployment</artifactId> <version>${camel-quarkus.version}</version> </dependency> + <dependency> + <groupId>org.apache.camel.quarkus</groupId> + <artifactId>camel-quarkus-jolokia</artifactId> + <version>${camel-quarkus.version}</version> + </dependency> + <dependency> + <groupId>org.apache.camel.quarkus</groupId> + <artifactId>camel-quarkus-jolokia-deployment</artifactId> + <version>${camel-quarkus.version}</version> + </dependency> <dependency> <groupId>org.apache.camel.quarkus</groupId> <artifactId>camel-quarkus-jolt</artifactId> @@ -7257,6 +7267,11 @@ <artifactId>kotlinx-serialization-core-jvm</artifactId> <version>${kotlinx.version}</version> </dependency> + <dependency> + <groupId>org.jolokia</groupId> + <artifactId>jolokia-agent-jvm</artifactId> + <version>${jolokia.version}</version> + </dependency> <dependency> <groupId>org.json</groupId> <artifactId>json</artifactId> diff --git a/poms/bom/src/main/generated/flattened-full-pom.xml b/poms/bom/src/main/generated/flattened-full-pom.xml index fb4ff3503f..665cb6aa7d 100644 --- a/poms/bom/src/main/generated/flattened-full-pom.xml +++ b/poms/bom/src/main/generated/flattened-full-pom.xml @@ -4431,6 +4431,16 @@ <artifactId>camel-quarkus-jms-deployment</artifactId><!-- org.apache.camel.quarkus:camel-quarkus-bom:${project.version} --> <version>3.19.0-SNAPSHOT</version><!-- org.apache.camel.quarkus:camel-quarkus-bom:${project.version} --> </dependency> + <dependency> + <groupId>org.apache.camel.quarkus</groupId><!-- org.apache.camel.quarkus:camel-quarkus-bom:${project.version} --> + <artifactId>camel-quarkus-jolokia</artifactId><!-- org.apache.camel.quarkus:camel-quarkus-bom:${project.version} --> + <version>3.19.0-SNAPSHOT</version><!-- org.apache.camel.quarkus:camel-quarkus-bom:${project.version} --> + </dependency> + <dependency> + <groupId>org.apache.camel.quarkus</groupId><!-- org.apache.camel.quarkus:camel-quarkus-bom:${project.version} --> + <artifactId>camel-quarkus-jolokia-deployment</artifactId><!-- org.apache.camel.quarkus:camel-quarkus-bom:${project.version} --> + <version>3.19.0-SNAPSHOT</version><!-- org.apache.camel.quarkus:camel-quarkus-bom:${project.version} --> + </dependency> <dependency> <groupId>org.apache.camel.quarkus</groupId><!-- org.apache.camel.quarkus:camel-quarkus-bom:${project.version} --> <artifactId>camel-quarkus-jolt</artifactId><!-- org.apache.camel.quarkus:camel-quarkus-bom:${project.version} --> @@ -7179,6 +7189,11 @@ <artifactId>kotlinx-serialization-core-jvm</artifactId><!-- org.apache.camel.quarkus:camel-quarkus-bom:${project.version} --> <version>1.4.0</version><!-- org.apache.camel.quarkus:camel-quarkus-bom:${project.version} --> </dependency> + <dependency> + <groupId>org.jolokia</groupId><!-- org.apache.camel.quarkus:camel-quarkus-bom:${project.version} --> + <artifactId>jolokia-agent-jvm</artifactId><!-- org.apache.camel.quarkus:camel-quarkus-bom:${project.version} --> + <version>2.2.2</version><!-- org.apache.camel.quarkus:camel-quarkus-bom:${project.version} --> + </dependency> <dependency> <groupId>org.json</groupId><!-- org.apache.camel.quarkus:camel-quarkus-bom:${project.version} --> <artifactId>json</artifactId><!-- org.apache.camel.quarkus:camel-quarkus-bom:${project.version} --> diff --git a/poms/bom/src/main/generated/flattened-reduced-pom.xml b/poms/bom/src/main/generated/flattened-reduced-pom.xml index 0b95d4e96c..3e6f4253c1 100644 --- a/poms/bom/src/main/generated/flattened-reduced-pom.xml +++ b/poms/bom/src/main/generated/flattened-reduced-pom.xml @@ -4421,6 +4421,16 @@ <artifactId>camel-quarkus-jms-deployment</artifactId> <version>3.19.0-SNAPSHOT</version> </dependency> + <dependency> + <groupId>org.apache.camel.quarkus</groupId> + <artifactId>camel-quarkus-jolokia</artifactId> + <version>3.19.0-SNAPSHOT</version> + </dependency> + <dependency> + <groupId>org.apache.camel.quarkus</groupId> + <artifactId>camel-quarkus-jolokia-deployment</artifactId> + <version>3.19.0-SNAPSHOT</version> + </dependency> <dependency> <groupId>org.apache.camel.quarkus</groupId> <artifactId>camel-quarkus-jolt</artifactId> @@ -7119,6 +7129,11 @@ <artifactId>kotlinx-serialization-core-jvm</artifactId> <version>1.4.0</version> </dependency> + <dependency> + <groupId>org.jolokia</groupId> + <artifactId>jolokia-agent-jvm</artifactId> + <version>2.2.2</version> + </dependency> <dependency> <groupId>org.json</groupId> <artifactId>json</artifactId> diff --git a/poms/bom/src/main/generated/flattened-reduced-verbose-pom.xml b/poms/bom/src/main/generated/flattened-reduced-verbose-pom.xml index c48e6d38bf..03a39e847f 100644 --- a/poms/bom/src/main/generated/flattened-reduced-verbose-pom.xml +++ b/poms/bom/src/main/generated/flattened-reduced-verbose-pom.xml @@ -4421,6 +4421,16 @@ <artifactId>camel-quarkus-jms-deployment</artifactId><!-- org.apache.camel.quarkus:camel-quarkus-bom:${project.version} --> <version>3.19.0-SNAPSHOT</version><!-- org.apache.camel.quarkus:camel-quarkus-bom:${project.version} --> </dependency> + <dependency> + <groupId>org.apache.camel.quarkus</groupId><!-- org.apache.camel.quarkus:camel-quarkus-bom:${project.version} --> + <artifactId>camel-quarkus-jolokia</artifactId><!-- org.apache.camel.quarkus:camel-quarkus-bom:${project.version} --> + <version>3.19.0-SNAPSHOT</version><!-- org.apache.camel.quarkus:camel-quarkus-bom:${project.version} --> + </dependency> + <dependency> + <groupId>org.apache.camel.quarkus</groupId><!-- org.apache.camel.quarkus:camel-quarkus-bom:${project.version} --> + <artifactId>camel-quarkus-jolokia-deployment</artifactId><!-- org.apache.camel.quarkus:camel-quarkus-bom:${project.version} --> + <version>3.19.0-SNAPSHOT</version><!-- org.apache.camel.quarkus:camel-quarkus-bom:${project.version} --> + </dependency> <dependency> <groupId>org.apache.camel.quarkus</groupId><!-- org.apache.camel.quarkus:camel-quarkus-bom:${project.version} --> <artifactId>camel-quarkus-jolt</artifactId><!-- org.apache.camel.quarkus:camel-quarkus-bom:${project.version} --> @@ -7119,6 +7129,11 @@ <artifactId>kotlinx-serialization-core-jvm</artifactId><!-- org.apache.camel.quarkus:camel-quarkus-bom:${project.version} --> <version>1.4.0</version><!-- org.apache.camel.quarkus:camel-quarkus-bom:${project.version} --> </dependency> + <dependency> + <groupId>org.jolokia</groupId><!-- org.apache.camel.quarkus:camel-quarkus-bom:${project.version} --> + <artifactId>jolokia-agent-jvm</artifactId><!-- org.apache.camel.quarkus:camel-quarkus-bom:${project.version} --> + <version>2.2.2</version><!-- org.apache.camel.quarkus:camel-quarkus-bom:${project.version} --> + </dependency> <dependency> <groupId>org.json</groupId><!-- org.apache.camel.quarkus:camel-quarkus-bom:${project.version} --> <artifactId>json</artifactId><!-- org.apache.camel.quarkus:camel-quarkus-bom:${project.version} -->