diff --git a/README.adoc b/README.adoc
index f5517ec3..110e73f9 100644
--- a/README.adoc
+++ b/README.adoc
@@ -27,7 +27,7 @@ readme's instructions.
=== Examples
// examples: START
-Number of Examples: 58 (0 deprecated)
+Number of Examples: 59 (0 deprecated)
[width="100%",cols="4,2,4",options="header"]
|===
@@ -77,6 +77,8 @@ Number of Examples: 58 (0 deprecated)
| link:master/readme.adoc[Master] (master) | Clustering | An example showing how to work with Camel's Master component and Spring Boot
+| link:rest-cxf-opentelemetry/README.adoc[Rest Cxf Opentelemetry] (rest-cxf-opentelemetry) | CXF | An example showing Camel REST using CXF and OpenTelemetry with Spring Boot
+
| link:soap-cxf/README.adoc[Soap Cxf] (soap-cxf) | CXF | An example showing the Camel SOAP CXF
| link:arangodb/README.adoc[Arangodb] (arangodb) | Database | An example showing the Camel ArangoDb component with Spring Boot
diff --git a/pom.xml b/pom.xml
index 318d1c9c..fb165cfd 100644
--- a/pom.xml
+++ b/pom.xml
@@ -69,6 +69,7 @@
reactive-streamsresilience4jrest-cxf
+ rest-cxf-opentelemetryrest-openapirest-openapi-simplerest-openapi-springdoc
diff --git a/rest-cxf-opentelemetry/README.adoc b/rest-cxf-opentelemetry/README.adoc
new file mode 100644
index 00000000..5d09ff6a
--- /dev/null
+++ b/rest-cxf-opentelemetry/README.adoc
@@ -0,0 +1,145 @@
+== Spring Boot Example with Camel exposing REST services using Apache CXF, collecting distributed tracing using OpenTelemetry
+
+=== Introduction
+
+This example illustrates how to use https://projects.spring.io/spring-boot/[Spring Boot] with http://camel.apache.org[Camel] and https://cxf.apache.org/[Apache CXF] implementing REST services using bottom-up approach.
+
+If you are using REST services directly from Camel and you want to use https://opentelemetry.io/[OpenTelemetry] please refer to link:../opentelemetry/README.adoc[the dedicated example]
+
+There are 3 services which communicate each other, starting from the `random` service:
+
+- random: the main service, exposes the entry point REST service and store the results
+- even: the service that verifies the even numbers
+- odd: the service that verifies the odd numbers
+
+moreover there is a common module containing common classes
+
+image::docs/overview.png[]
+
+=== Build
+
+You can build this example using the following command; it will also download the OpenTelemetry agent used to instrument the applications:
+
+ $ mvn package -Potel-agent
+
+=== Run
+
+Run docker-compose to start all the needed services:
+
+ $ docker-compose -f containers/docker-compose.yml up -d
+
+the command runs:
+
+- https://github.com/minio/minio[minio] as application storage
+- https://github.com/open-telemetry/opentelemetry-collector[opentelemetry-collector] to receive the generated Trace/Span
+- https://github.com/jaegertracing/jaeger[Jaeger] to visualize the traces
+
+Run each services on separated terminals:
+
+ $ source containers/env.sh \
+ && java -javaagent:target/opentelemetry-javaagent.jar \
+ -Dotel.service.name=random \
+ -jar rest-cxf-otel-random/target/*.jar
+
+ $ source containers/env.sh \
+ && java -javaagent:target/opentelemetry-javaagent.jar \
+ -Dotel.service.name=even \
+ -Dserver.port=8081 \
+ -jar rest-cxf-otel-even/target/*.jar
+
+ $ source containers/env.sh \
+ && java -javaagent:target/opentelemetry-javaagent.jar \
+ -Dotel.service.name=odd \
+ -Dserver.port=8082 \
+ -jar rest-cxf-otel-odd/target/*.jar
+
+After the Spring Boot applications have been started, you can open the following URL in your web browser http://localhost:8080/services/ to access the list of services, including WADL definition
+
+You can also access the REST endpoint from the command line:
+
+i.e. to generate 5 random numbers run:
+
+ $ curl -X POST http://localhost:8080/services/api/play/5 -s | jq .
+
+The command will produce an output like:
+
+[source,json]
+----
+{
+ "result": {
+ "ODD": [
+ {
+ "number": 229,
+ "type": "ODD"
+ },
+ {
+ "number": 585,
+ "type": "ODD"
+ }
+ ],
+ "EVEN": [
+ {
+ "number": 648,
+ "type": "EVEN"
+ },
+ {
+ "number": 670,
+ "type": "EVEN"
+ },
+ {
+ "number": 846,
+ "type": "EVEN"
+ }
+ ]
+ },
+ "evenCount": 3,
+ "oddCount": 2
+}
+----
+
+Now in the Jaeger UI http://localhost:16686 the traces will be available, both from Camel and from CXF instrumentation
+
+image::docs/jaeger.png[]
+
+The services can be stopped pressing `[CTRL] + [C]` in the shell, and the containers can be stopped with
+
+ $ docker-compose -f containers/docker-compose.yml down
+
+=== Explain the code
+
+The dependency `org.apache.cxf:cxf-integration-tracing-opentelemetry` provides `org.apache.cxf.tracing.opentelemetry.jaxrs.OpenTelemetryFeature` that instruments the OpenTelemetry API implemented in the java agent.
+
+The feature is injected in the Spring context
+
+[source,java]
+----
+include::rest-cxf-otel-common/src/main/java/org/apache/camel/example/springboot/cxf/otel/CxfConfig.java[lines=35..38]
+----
+
+and then it is configured in the Camel route as provider in the `cxfrs` endpoint
+
+[source,java]
+----
+include::rest-cxf-otel-random/src/main/java/org/apache/camel/example/springboot/cxf/otel/CamelRouter.java[lines=51]
+----
+
+When the feature is enabled it is possible to add custom tags or logs using `TracerContext` from CXF
+[source,java]
+----
+include::rest-cxf-otel-random/src/main/java/org/apache/camel/example/springboot/cxf/otel/RandomService.java[lines=56..58]
+----
+
+[source,java]
+----
+include::rest-cxf-otel-random/src/main/java/org/apache/camel/example/springboot/cxf/otel/RandomServiceImpl.java[lines=43..45]
+----
+
+=== Help and contributions
+
+If you hit any problem using Camel or have some feedback, then please
+https://camel.apache.org/community/support/[let us know].
+
+We also love contributors, so
+https://camel.apache.org/community/contributing/[get involved] :-)
+
+The Camel riders!
diff --git a/rest-cxf-opentelemetry/containers/docker-compose.yml b/rest-cxf-opentelemetry/containers/docker-compose.yml
new file mode 100644
index 00000000..25b7e9d5
--- /dev/null
+++ b/rest-cxf-opentelemetry/containers/docker-compose.yml
@@ -0,0 +1,35 @@
+version: "3.9"
+
+services:
+
+ minio:
+ image: "quay.io/minio/minio:latest"
+ environment:
+ - MINIO_ROOT_USER=admin
+ - MINIO_ROOT_PASSWORD=admin123
+ ports:
+ - "9000:9000"
+ - "9001:9001"
+ command:
+ - server
+ - /data
+ - --console-address
+ - :9001
+
+ otel-collector:
+ image: otel/opentelemetry-collector:latest
+ command:
+ - --config=/etc/otelcol-cont/otel-collector.yml
+ volumes:
+ - ./otel-collector.yml:/etc/otelcol-cont/otel-collector.yml
+ ports:
+ - "4318:4318" # OTLP http receiver
+ - "4317:4317" # OTLP grpc receiver
+ depends_on:
+ - jaeger-all-in-one
+
+ jaeger-all-in-one:
+ image: quay.io/jaegertracing/all-in-one:latest
+ restart: always
+ ports:
+ - "16686:16686"
diff --git a/rest-cxf-opentelemetry/containers/env.sh b/rest-cxf-opentelemetry/containers/env.sh
new file mode 100644
index 00000000..549fb238
--- /dev/null
+++ b/rest-cxf-opentelemetry/containers/env.sh
@@ -0,0 +1,8 @@
+#!/usr/bin/bash
+
+export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317
+export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://localhost:4318/v1/traces
+export OTEL_TRACES_EXPORTER=otlp
+export OTEL_METRICS_EXPORTER=none
+export OTEL_LOGS_EXPORTER=none
+#export OTEL_JAVAAGENT_DEBUG=true
diff --git a/rest-cxf-opentelemetry/containers/otel-collector.yml b/rest-cxf-opentelemetry/containers/otel-collector.yml
new file mode 100644
index 00000000..db8a4beb
--- /dev/null
+++ b/rest-cxf-opentelemetry/containers/otel-collector.yml
@@ -0,0 +1,25 @@
+receivers:
+ otlp:
+ protocols:
+ grpc:
+ endpoint: 0.0.0.0:4317
+ http:
+ endpoint: 0.0.0.0:4318
+
+processors:
+ batch:
+
+exporters:
+ debug:
+ verbosity: detailed
+ otlp/jaeger:
+ endpoint: jaeger-all-in-one:4317
+ tls:
+ insecure: true
+
+service:
+ pipelines:
+ traces:
+ receivers: [otlp]
+ processors: [batch]
+ exporters: [debug,otlp/jaeger]
diff --git a/rest-cxf-opentelemetry/docs/jaeger.png b/rest-cxf-opentelemetry/docs/jaeger.png
new file mode 100644
index 00000000..e89e987d
Binary files /dev/null and b/rest-cxf-opentelemetry/docs/jaeger.png differ
diff --git a/rest-cxf-opentelemetry/docs/overview.png b/rest-cxf-opentelemetry/docs/overview.png
new file mode 100644
index 00000000..15550cd6
Binary files /dev/null and b/rest-cxf-opentelemetry/docs/overview.png differ
diff --git a/rest-cxf-opentelemetry/pom.xml b/rest-cxf-opentelemetry/pom.xml
new file mode 100644
index 00000000..7d6adcdc
--- /dev/null
+++ b/rest-cxf-opentelemetry/pom.xml
@@ -0,0 +1,163 @@
+
+
+ 4.0.0
+
+
+ org.apache.camel.springboot.example
+ examples
+ 4.8.0-SNAPSHOT
+
+
+ camel-example-spring-boot-rest-cxf-opentelemetry
+ Camel SB Examples :: REST using CXF and OpenTelemetry
+ An example showing Camel REST using CXF and OpenTelemetry with Spring Boot
+ pom
+
+
+ CXF
+ 2.7.0
+
+
+
+ rest-cxf-otel-common
+ rest-cxf-otel-random
+ rest-cxf-otel-even
+ rest-cxf-otel-odd
+
+
+
+
+
+
+ org.apache.camel.springboot
+ camel-spring-boot-bom
+ ${project.version}
+ pom
+ import
+
+
+ org.springframework.boot
+ spring-boot-dependencies
+ ${spring-boot-version}
+ pom
+ import
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ org.apache.camel.springboot
+ camel-direct-starter
+
+
+ org.apache.camel.springboot
+ camel-log-starter
+
+
+ org.apache.camel.springboot
+ camel-bean-starter
+
+
+ org.apache.camel.springboot
+ camel-bean-validator-starter
+
+
+ org.apache.camel.springboot
+ camel-cxf-rest-starter
+
+
+ org.apache.camel.springboot
+ camel-jackson-starter
+
+
+
+
+ org.apache.camel.springboot
+ camel-opentelemetry-starter
+
+
+
+
+ com.fasterxml.jackson.jakarta.rs
+ jackson-jakarta-rs-json-provider
+
+
+
+
+ org.apache.cxf
+ cxf-integration-tracing-opentelemetry
+ ${cxf-version}
+
+
+
+
+ org.apache.cxf
+ cxf-rt-rs-service-description
+ ${cxf-version}
+
+
+
+
+
+
+ otel-agent
+
+ false
+
+
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+
+
+
+ copy
+
+ process-resources
+
+ true
+
+
+ io.opentelemetry.javaagent
+ opentelemetry-javaagent
+ ${opentelemetry-javaagent.version}
+ jar
+ ${project.build.directory}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/rest-cxf-opentelemetry/rest-cxf-otel-common/pom.xml b/rest-cxf-opentelemetry/rest-cxf-otel-common/pom.xml
new file mode 100644
index 00000000..e5995f46
--- /dev/null
+++ b/rest-cxf-opentelemetry/rest-cxf-otel-common/pom.xml
@@ -0,0 +1,78 @@
+
+
+
+
+ 4.0.0
+
+
+ org.apache.camel.springboot.example
+ camel-example-spring-boot-rest-cxf-opentelemetry
+ 4.8.0-SNAPSHOT
+
+
+ camel-example-spring-boot-rest-cxf-otel-common
+ Camel SB Examples :: REST using CXF and OpenTelemetry :: Common
+
+
+ CXF
+
+
+
+
+
+ org.apache.camel
+ camel-component-maven-plugin
+ ${camel-version}
+
+
+ generate
+
+ generate
+
+ process-classes
+
+
+
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+
+
+ initialize
+
+ add-source
+ add-resource
+
+
+
+
+
+
+
+ src/generated/resources
+
+
+
+
+
+
+
+
+
diff --git a/rest-cxf-opentelemetry/rest-cxf-otel-common/src/generated/java/org/apache/camel/example/springboot/cxf/otel/InputStreamConverterLoader.java b/rest-cxf-opentelemetry/rest-cxf-otel-common/src/generated/java/org/apache/camel/example/springboot/cxf/otel/InputStreamConverterLoader.java
new file mode 100644
index 00000000..daab67b2
--- /dev/null
+++ b/rest-cxf-opentelemetry/rest-cxf-otel-common/src/generated/java/org/apache/camel/example/springboot/cxf/otel/InputStreamConverterLoader.java
@@ -0,0 +1,54 @@
+/* Generated by camel build tools - do NOT edit this file! */
+package org.apache.camel.example.springboot.cxf.otel;
+
+import javax.annotation.processing.Generated;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.CamelContextAware;
+import org.apache.camel.DeferredContextBinding;
+import org.apache.camel.Exchange;
+import org.apache.camel.TypeConversionException;
+import org.apache.camel.TypeConverterLoaderException;
+import org.apache.camel.spi.TypeConverterLoader;
+import org.apache.camel.spi.TypeConverterRegistry;
+import org.apache.camel.support.SimpleTypeConverter;
+import org.apache.camel.support.TypeConverterSupport;
+import org.apache.camel.util.DoubleMap;
+
+/**
+ * Generated by camel build tools - do NOT edit this file!
+ */
+@Generated("org.apache.camel.maven.packaging.TypeConverterLoaderGeneratorMojo")
+@SuppressWarnings("unchecked")
+@DeferredContextBinding
+public final class InputStreamConverterLoader implements TypeConverterLoader, CamelContextAware {
+
+ private CamelContext camelContext;
+
+ public InputStreamConverterLoader() {
+ }
+
+ @Override
+ public void setCamelContext(CamelContext camelContext) {
+ this.camelContext = camelContext;
+ }
+
+ @Override
+ public CamelContext getCamelContext() {
+ return camelContext;
+ }
+
+ @Override
+ public void load(TypeConverterRegistry registry) throws TypeConverterLoaderException {
+ registerConverters(registry);
+ }
+
+ private void registerConverters(TypeConverterRegistry registry) {
+ addTypeConverter(registry, org.apache.camel.example.springboot.cxf.otel.RandomNumber.class, java.io.InputStream.class, false,
+ (type, exchange, value) -> org.apache.camel.example.springboot.cxf.otel.InputStreamConverter.toRandomNumber((java.io.InputStream) value));
+ }
+
+ private static void addTypeConverter(TypeConverterRegistry registry, Class> toType, Class> fromType, boolean allowNull, SimpleTypeConverter.ConversionMethod method) {
+ registry.addTypeConverter(toType, fromType, new SimpleTypeConverter(allowNull, method));
+ }
+}
diff --git a/rest-cxf-opentelemetry/rest-cxf-otel-common/src/generated/java/org/apache/camel/example/springboot/cxf/otel/IntegerConverterLoader.java b/rest-cxf-opentelemetry/rest-cxf-otel-common/src/generated/java/org/apache/camel/example/springboot/cxf/otel/IntegerConverterLoader.java
new file mode 100644
index 00000000..fe86ec1e
--- /dev/null
+++ b/rest-cxf-opentelemetry/rest-cxf-otel-common/src/generated/java/org/apache/camel/example/springboot/cxf/otel/IntegerConverterLoader.java
@@ -0,0 +1,54 @@
+/* Generated by camel build tools - do NOT edit this file! */
+package org.apache.camel.example.springboot.cxf.otel;
+
+import javax.annotation.processing.Generated;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.CamelContextAware;
+import org.apache.camel.DeferredContextBinding;
+import org.apache.camel.Exchange;
+import org.apache.camel.TypeConversionException;
+import org.apache.camel.TypeConverterLoaderException;
+import org.apache.camel.spi.TypeConverterLoader;
+import org.apache.camel.spi.TypeConverterRegistry;
+import org.apache.camel.support.SimpleTypeConverter;
+import org.apache.camel.support.TypeConverterSupport;
+import org.apache.camel.util.DoubleMap;
+
+/**
+ * Generated by camel build tools - do NOT edit this file!
+ */
+@Generated("org.apache.camel.maven.packaging.TypeConverterLoaderGeneratorMojo")
+@SuppressWarnings("unchecked")
+@DeferredContextBinding
+public final class IntegerConverterLoader implements TypeConverterLoader, CamelContextAware {
+
+ private CamelContext camelContext;
+
+ public IntegerConverterLoader() {
+ }
+
+ @Override
+ public void setCamelContext(CamelContext camelContext) {
+ this.camelContext = camelContext;
+ }
+
+ @Override
+ public CamelContext getCamelContext() {
+ return camelContext;
+ }
+
+ @Override
+ public void load(TypeConverterRegistry registry) throws TypeConverterLoaderException {
+ registerConverters(registry);
+ }
+
+ private void registerConverters(TypeConverterRegistry registry) {
+ addTypeConverter(registry, org.apache.camel.example.springboot.cxf.otel.RandomNumber.class, java.lang.Integer.class, false,
+ (type, exchange, value) -> org.apache.camel.example.springboot.cxf.otel.IntegerConverter.toRandomNumber((java.lang.Integer) value));
+ }
+
+ private static void addTypeConverter(TypeConverterRegistry registry, Class> toType, Class> fromType, boolean allowNull, SimpleTypeConverter.ConversionMethod method) {
+ registry.addTypeConverter(toType, fromType, new SimpleTypeConverter(allowNull, method));
+ }
+}
diff --git a/rest-cxf-opentelemetry/rest-cxf-otel-common/src/generated/resources/META-INF/services/org/apache/camel/TypeConverterLoader b/rest-cxf-opentelemetry/rest-cxf-otel-common/src/generated/resources/META-INF/services/org/apache/camel/TypeConverterLoader
new file mode 100644
index 00000000..b01c01ee
--- /dev/null
+++ b/rest-cxf-opentelemetry/rest-cxf-otel-common/src/generated/resources/META-INF/services/org/apache/camel/TypeConverterLoader
@@ -0,0 +1,3 @@
+# Generated by camel build tools - do NOT edit this file!
+org.apache.camel.example.springboot.cxf.otel.InputStreamConverterLoader
+org.apache.camel.example.springboot.cxf.otel.IntegerConverterLoader
diff --git a/rest-cxf-opentelemetry/rest-cxf-otel-common/src/main/java/org/apache/camel/example/springboot/cxf/otel/Constants.java b/rest-cxf-opentelemetry/rest-cxf-otel-common/src/main/java/org/apache/camel/example/springboot/cxf/otel/Constants.java
new file mode 100644
index 00000000..00401086
--- /dev/null
+++ b/rest-cxf-opentelemetry/rest-cxf-otel-common/src/main/java/org/apache/camel/example/springboot/cxf/otel/Constants.java
@@ -0,0 +1,30 @@
+/*
+ * 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.example.springboot.cxf.otel;
+
+public final class Constants {
+
+ private Constants() {
+ }
+
+ public static final String SERVICE_HEADER_NAME = "service-name";
+
+ public enum NUM_TYPE {
+ EVEN,
+ ODD
+ }
+}
diff --git a/rest-cxf-opentelemetry/rest-cxf-otel-common/src/main/java/org/apache/camel/example/springboot/cxf/otel/CxfConfig.java b/rest-cxf-opentelemetry/rest-cxf-otel-common/src/main/java/org/apache/camel/example/springboot/cxf/otel/CxfConfig.java
new file mode 100644
index 00000000..d1a1751d
--- /dev/null
+++ b/rest-cxf-opentelemetry/rest-cxf-otel-common/src/main/java/org/apache/camel/example/springboot/cxf/otel/CxfConfig.java
@@ -0,0 +1,39 @@
+/*
+ * 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.example.springboot.cxf.otel;
+
+import org.apache.cxf.tracing.opentelemetry.jaxrs.OpenTelemetryFeature;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.jakarta.rs.json.JacksonJsonProvider;
+
+@Configuration
+public class CxfConfig {
+
+ @Bean
+ public JacksonJsonProvider jaxrsProvider() {
+ return new JacksonJsonProvider().configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
+ }
+
+ @Bean
+ public OpenTelemetryFeature openTelemetryProvider() {
+ return new OpenTelemetryFeature();
+ }
+}
diff --git a/rest-cxf-opentelemetry/rest-cxf-otel-common/src/main/java/org/apache/camel/example/springboot/cxf/otel/InputStreamConverter.java b/rest-cxf-opentelemetry/rest-cxf-otel-common/src/main/java/org/apache/camel/example/springboot/cxf/otel/InputStreamConverter.java
new file mode 100644
index 00000000..d5137d6d
--- /dev/null
+++ b/rest-cxf-opentelemetry/rest-cxf-otel-common/src/main/java/org/apache/camel/example/springboot/cxf/otel/InputStreamConverter.java
@@ -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.
+ */
+package org.apache.camel.example.springboot.cxf.otel;
+
+import org.apache.camel.Converter;
+import org.apache.camel.TypeConverters;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+@Converter(generateLoader = true)
+public class InputStreamConverter implements TypeConverters {
+
+ @Converter
+ public static RandomNumber toRandomNumber(InputStream inputStream) throws IOException {
+ return new ObjectMapper().readValue(inputStream, RandomNumber.class);
+ }
+}
diff --git a/rest-cxf-opentelemetry/rest-cxf-otel-common/src/main/java/org/apache/camel/example/springboot/cxf/otel/IntegerConverter.java b/rest-cxf-opentelemetry/rest-cxf-otel-common/src/main/java/org/apache/camel/example/springboot/cxf/otel/IntegerConverter.java
new file mode 100644
index 00000000..ac5e361c
--- /dev/null
+++ b/rest-cxf-opentelemetry/rest-cxf-otel-common/src/main/java/org/apache/camel/example/springboot/cxf/otel/IntegerConverter.java
@@ -0,0 +1,29 @@
+/*
+ * 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.example.springboot.cxf.otel;
+
+import org.apache.camel.Converter;
+import org.apache.camel.TypeConverters;
+
+@Converter(generateLoader = true)
+public class IntegerConverter implements TypeConverters {
+
+ @Converter
+ public static RandomNumber toRandomNumber(Integer num) {
+ return new RandomNumber(num);
+ }
+}
diff --git a/rest-cxf-opentelemetry/rest-cxf-otel-common/src/main/java/org/apache/camel/example/springboot/cxf/otel/RandomNumber.java b/rest-cxf-opentelemetry/rest-cxf-otel-common/src/main/java/org/apache/camel/example/springboot/cxf/otel/RandomNumber.java
new file mode 100644
index 00000000..c258f2f0
--- /dev/null
+++ b/rest-cxf-opentelemetry/rest-cxf-otel-common/src/main/java/org/apache/camel/example/springboot/cxf/otel/RandomNumber.java
@@ -0,0 +1,73 @@
+/*
+ * 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.example.springboot.cxf.otel;
+
+import java.util.Objects;
+import java.util.Optional;
+
+import jakarta.validation.constraints.NotNull;
+
+public class RandomNumber {
+
+ @NotNull
+ Integer number;
+
+ Constants.NUM_TYPE type;
+
+ public RandomNumber() {
+ }
+
+ public RandomNumber(final Integer number) {
+ this.number = number;
+ }
+
+ public Integer getNumber() {
+ return number;
+ }
+
+ public RandomNumber setNumber(final Integer number) {
+ this.number = number;
+ return this;
+ }
+
+ public Constants.NUM_TYPE getType() {
+ return type;
+ }
+
+ public RandomNumber setType(final Constants.NUM_TYPE type) {
+ this.type = type;
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return Optional.ofNullable(number).map(Objects::toString).orElse("");
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ final RandomNumber that = (RandomNumber) o;
+ return Objects.equals(getNumber(), that.getNumber());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(getNumber());
+ }
+}
diff --git a/rest-cxf-opentelemetry/rest-cxf-otel-even/pom.xml b/rest-cxf-opentelemetry/rest-cxf-otel-even/pom.xml
new file mode 100644
index 00000000..f80eac93
--- /dev/null
+++ b/rest-cxf-opentelemetry/rest-cxf-otel-even/pom.xml
@@ -0,0 +1,63 @@
+
+
+
+
+ 4.0.0
+
+
+ org.apache.camel.springboot.example
+ camel-example-spring-boot-rest-cxf-opentelemetry
+ 4.8.0-SNAPSHOT
+
+
+ camel-example-spring-boot-rest-cxf-otel-even
+ Camel SB Examples :: REST using CXF and OpenTelemetry :: Even
+
+
+ CXF
+
+
+
+
+ org.apache.camel.springboot.example
+ camel-example-spring-boot-rest-cxf-otel-common
+ ${project.version}
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+ ${spring-boot-version}
+
+
+
+ repackage
+
+
+
+
+
+
+
+
diff --git a/rest-cxf-opentelemetry/rest-cxf-otel-even/src/main/java/org/apache/camel/example/springboot/cxf/otel/Application.java b/rest-cxf-opentelemetry/rest-cxf-otel-even/src/main/java/org/apache/camel/example/springboot/cxf/otel/Application.java
new file mode 100644
index 00000000..15b7d453
--- /dev/null
+++ b/rest-cxf-opentelemetry/rest-cxf-otel-even/src/main/java/org/apache/camel/example/springboot/cxf/otel/Application.java
@@ -0,0 +1,37 @@
+/*
+ * 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.example.springboot.cxf.otel;
+
+import org.apache.camel.opentelemetry.starter.CamelOpenTelemetry;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+// CHECKSTYLE:OFF
+@SpringBootApplication
+@CamelOpenTelemetry
+public class Application {
+
+ /**
+ * Main method to start the application.
+ */
+ public static void main(String[] args) {
+ SpringApplication.run(Application.class, args);
+ }
+
+}
+// CHECKSTYLE:ON
diff --git a/rest-cxf-opentelemetry/rest-cxf-otel-even/src/main/java/org/apache/camel/example/springboot/cxf/otel/CamelRouter.java b/rest-cxf-opentelemetry/rest-cxf-otel-even/src/main/java/org/apache/camel/example/springboot/cxf/otel/CamelRouter.java
new file mode 100644
index 00000000..c2aa5675
--- /dev/null
+++ b/rest-cxf-opentelemetry/rest-cxf-otel-even/src/main/java/org/apache/camel/example/springboot/cxf/otel/CamelRouter.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.example.springboot.cxf.otel;
+
+import static org.apache.camel.example.springboot.cxf.otel.Constants.SERVICE_HEADER_NAME;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.bean.validator.BeanValidationException;
+import org.apache.camel.component.cxf.common.message.CxfConstants;
+import org.apache.camel.model.dataformat.JsonLibrary;
+
+import org.springframework.stereotype.Component;
+
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+
+@Component
+public class CamelRouter extends RouteBuilder {
+
+ @Override
+ public void configure() throws Exception {
+ //very raw way, just to handle the validation responses
+ onException(BeanValidationException.class)
+ .handled(true)
+ .setHeader(Exchange.HTTP_RESPONSE_CODE, constant(Response.Status.BAD_REQUEST.getStatusCode()))
+ .setBody(simple("${exchangeProperty.CamelExceptionCaught.getMessage()}"));
+
+ // @formatter:off
+ from("cxfrs:/api?" +
+ "resourceClasses=org.apache.camel.example.springboot.cxf.otel.EvenService" +
+ "&bindingStyle=SimpleConsumer" +
+ "&providers=jaxrsProvider,openTelemetryProvider" +
+ "&loggingFeatureEnabled=true")
+ .to("log:camel-cxf-log?showAll=true")
+ .setHeader(Exchange.BEAN_METHOD_NAME, simple("${header.operationName}"))
+ .bean(EvenServiceImpl.class);
+
+
+ from("direct:register").routeId("even-register")
+ .marshal().json(JsonLibrary.Jackson)
+ .setHeader(CxfConstants.HTTP_METHOD, constant("PUT"))
+ .setHeader(CxfConstants.CONTENT_TYPE, constant(MediaType.APPLICATION_JSON))
+ .setHeader(SERVICE_HEADER_NAME, constant("even"))
+ .toD("cxfrs:{{service.random.url}}/services/api/register");
+ // @formatter:on
+ }
+
+}
diff --git a/rest-cxf-opentelemetry/rest-cxf-otel-even/src/main/java/org/apache/camel/example/springboot/cxf/otel/EvenService.java b/rest-cxf-opentelemetry/rest-cxf-otel-even/src/main/java/org/apache/camel/example/springboot/cxf/otel/EvenService.java
new file mode 100644
index 00000000..bf0a62db
--- /dev/null
+++ b/rest-cxf-opentelemetry/rest-cxf-otel-even/src/main/java/org/apache/camel/example/springboot/cxf/otel/EvenService.java
@@ -0,0 +1,43 @@
+/*
+ * 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.example.springboot.cxf.otel;
+
+import jakarta.validation.Valid;
+import jakarta.ws.rs.Consumes;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.core.MediaType;
+
+/**
+ * Service interface for managing users.
+ */
+@Consumes(MediaType.APPLICATION_JSON)
+@Produces(MediaType.APPLICATION_JSON)
+public interface EvenService {
+
+ /**
+ * Check if the number is even, and eventually, register it
+ *
+ * @param number
+ * the number to verify
+ */
+ @POST
+ @Path("/check")
+ RandomNumber check(@Valid RandomNumber number);
+
+}
diff --git a/rest-cxf-opentelemetry/rest-cxf-otel-even/src/main/java/org/apache/camel/example/springboot/cxf/otel/EvenServiceImpl.java b/rest-cxf-opentelemetry/rest-cxf-otel-even/src/main/java/org/apache/camel/example/springboot/cxf/otel/EvenServiceImpl.java
new file mode 100644
index 00000000..446bf593
--- /dev/null
+++ b/rest-cxf-opentelemetry/rest-cxf-otel-even/src/main/java/org/apache/camel/example/springboot/cxf/otel/EvenServiceImpl.java
@@ -0,0 +1,42 @@
+/*
+ * 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.example.springboot.cxf.otel;
+
+import org.apache.camel.ProducerTemplate;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+
+public class EvenServiceImpl implements EvenService {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(EvenServiceImpl.class);
+
+ @Autowired
+ ProducerTemplate producerTemplate;
+
+ @Override
+ public RandomNumber check(RandomNumber number) {
+ if (number.getNumber() % 2 == 0) {
+ //register
+ producerTemplate.sendBody("direct:register", new RandomNumber().setNumber(number.getNumber()).setType(Constants.NUM_TYPE.EVEN));
+ } else {
+ LOGGER.info("skip {}, it is odd", number);
+ }
+ return number;
+ }
+}
diff --git a/rest-cxf-opentelemetry/rest-cxf-otel-even/src/main/resources/application.properties b/rest-cxf-opentelemetry/rest-cxf-otel-even/src/main/resources/application.properties
new file mode 100644
index 00000000..4e64700f
--- /dev/null
+++ b/rest-cxf-opentelemetry/rest-cxf-otel-even/src/main/resources/application.properties
@@ -0,0 +1,22 @@
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+
+# the name of Camel
+camel.springboot.name = cxf-otel-even
+camel.springboot.main-run-controller=true
+service.random.url = http://localhost:8080
+#logging.level.org.apache.cxf = TRACE
diff --git a/rest-cxf-opentelemetry/rest-cxf-otel-odd/pom.xml b/rest-cxf-opentelemetry/rest-cxf-otel-odd/pom.xml
new file mode 100644
index 00000000..83fb364f
--- /dev/null
+++ b/rest-cxf-opentelemetry/rest-cxf-otel-odd/pom.xml
@@ -0,0 +1,63 @@
+
+
+
+
+ 4.0.0
+
+
+ org.apache.camel.springboot.example
+ camel-example-spring-boot-rest-cxf-opentelemetry
+ 4.8.0-SNAPSHOT
+
+
+ camel-example-spring-boot-rest-cxf-otel-odd
+ Camel SB Examples :: REST using CXF and OpenTelemetry :: Odd
+
+
+ CXF
+
+
+
+
+ org.apache.camel.springboot.example
+ camel-example-spring-boot-rest-cxf-otel-common
+ ${project.version}
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+ ${spring-boot-version}
+
+
+
+ repackage
+
+
+
+
+
+
+
+
diff --git a/rest-cxf-opentelemetry/rest-cxf-otel-odd/src/main/java/org/apache/camel/example/springboot/cxf/otel/Application.java b/rest-cxf-opentelemetry/rest-cxf-otel-odd/src/main/java/org/apache/camel/example/springboot/cxf/otel/Application.java
new file mode 100644
index 00000000..15b7d453
--- /dev/null
+++ b/rest-cxf-opentelemetry/rest-cxf-otel-odd/src/main/java/org/apache/camel/example/springboot/cxf/otel/Application.java
@@ -0,0 +1,37 @@
+/*
+ * 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.example.springboot.cxf.otel;
+
+import org.apache.camel.opentelemetry.starter.CamelOpenTelemetry;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+// CHECKSTYLE:OFF
+@SpringBootApplication
+@CamelOpenTelemetry
+public class Application {
+
+ /**
+ * Main method to start the application.
+ */
+ public static void main(String[] args) {
+ SpringApplication.run(Application.class, args);
+ }
+
+}
+// CHECKSTYLE:ON
diff --git a/rest-cxf-opentelemetry/rest-cxf-otel-odd/src/main/java/org/apache/camel/example/springboot/cxf/otel/CamelRouter.java b/rest-cxf-opentelemetry/rest-cxf-otel-odd/src/main/java/org/apache/camel/example/springboot/cxf/otel/CamelRouter.java
new file mode 100644
index 00000000..d355dc00
--- /dev/null
+++ b/rest-cxf-opentelemetry/rest-cxf-otel-odd/src/main/java/org/apache/camel/example/springboot/cxf/otel/CamelRouter.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.example.springboot.cxf.otel;
+
+import static org.apache.camel.example.springboot.cxf.otel.Constants.SERVICE_HEADER_NAME;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.bean.validator.BeanValidationException;
+import org.apache.camel.component.cxf.common.message.CxfConstants;
+import org.apache.camel.model.dataformat.JsonLibrary;
+
+import org.springframework.stereotype.Component;
+
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+
+@Component
+public class CamelRouter extends RouteBuilder {
+
+ @Override
+ public void configure() throws Exception {
+ //very raw way, just to handle the validation responses
+ onException(BeanValidationException.class)
+ .handled(true)
+ .setHeader(Exchange.HTTP_RESPONSE_CODE, constant(Response.Status.BAD_REQUEST.getStatusCode()))
+ .setBody(simple("${exchangeProperty.CamelExceptionCaught.getMessage()}"));
+
+ // @formatter:off
+ from("cxfrs:/api?" +
+ "resourceClasses=org.apache.camel.example.springboot.cxf.otel.OddService" +
+ "&bindingStyle=SimpleConsumer" +
+ "&providers=jaxrsProvider,openTelemetryProvider" +
+ "&loggingFeatureEnabled=true")
+ .to("log:camel-cxf-log?showAll=true")
+ .setHeader(Exchange.BEAN_METHOD_NAME, simple("${header.operationName}"))
+ .bean(OddServiceImpl.class);
+
+
+ from("direct:register").routeId("odd-register")
+ .marshal().json(JsonLibrary.Jackson)
+ .setHeader(CxfConstants.HTTP_METHOD, constant("PUT"))
+ .setHeader(CxfConstants.CONTENT_TYPE, constant(MediaType.APPLICATION_JSON))
+ .setHeader(SERVICE_HEADER_NAME, constant("odd"))
+ .toD("cxfrs:{{service.random.url}}/services/api/register");
+ // @formatter:on
+ }
+
+}
diff --git a/rest-cxf-opentelemetry/rest-cxf-otel-odd/src/main/java/org/apache/camel/example/springboot/cxf/otel/OddService.java b/rest-cxf-opentelemetry/rest-cxf-otel-odd/src/main/java/org/apache/camel/example/springboot/cxf/otel/OddService.java
new file mode 100644
index 00000000..4ecdce5e
--- /dev/null
+++ b/rest-cxf-opentelemetry/rest-cxf-otel-odd/src/main/java/org/apache/camel/example/springboot/cxf/otel/OddService.java
@@ -0,0 +1,43 @@
+/*
+ * 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.example.springboot.cxf.otel;
+
+import jakarta.validation.Valid;
+import jakarta.ws.rs.Consumes;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.core.MediaType;
+
+/**
+ * Service interface for managing users.
+ */
+@Consumes(MediaType.APPLICATION_JSON)
+@Produces(MediaType.APPLICATION_JSON)
+public interface OddService {
+
+ /**
+ * Check if the number is odd, and eventually, register it
+ *
+ * @param number
+ * the number to verify
+ */
+ @POST
+ @Path("/check")
+ RandomNumber check(@Valid RandomNumber number);
+
+}
diff --git a/rest-cxf-opentelemetry/rest-cxf-otel-odd/src/main/java/org/apache/camel/example/springboot/cxf/otel/OddServiceImpl.java b/rest-cxf-opentelemetry/rest-cxf-otel-odd/src/main/java/org/apache/camel/example/springboot/cxf/otel/OddServiceImpl.java
new file mode 100644
index 00000000..bab2b46d
--- /dev/null
+++ b/rest-cxf-opentelemetry/rest-cxf-otel-odd/src/main/java/org/apache/camel/example/springboot/cxf/otel/OddServiceImpl.java
@@ -0,0 +1,42 @@
+/*
+ * 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.example.springboot.cxf.otel;
+
+import org.apache.camel.ProducerTemplate;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+
+public class OddServiceImpl implements OddService {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(OddServiceImpl.class);
+
+ @Autowired
+ ProducerTemplate producerTemplate;
+
+ @Override
+ public RandomNumber check(RandomNumber number) {
+ if (number.getNumber() % 2 == 1) {
+ //register
+ producerTemplate.sendBody("direct:register", new RandomNumber().setNumber(number.getNumber()).setType(Constants.NUM_TYPE.ODD));
+ } else {
+ LOGGER.info("skip {}, it is even", number);
+ }
+ return number;
+ }
+}
diff --git a/rest-cxf-opentelemetry/rest-cxf-otel-odd/src/main/resources/application.properties b/rest-cxf-opentelemetry/rest-cxf-otel-odd/src/main/resources/application.properties
new file mode 100644
index 00000000..0a6755a3
--- /dev/null
+++ b/rest-cxf-opentelemetry/rest-cxf-otel-odd/src/main/resources/application.properties
@@ -0,0 +1,22 @@
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+
+# the name of Camel
+camel.springboot.name = cxf-otel-odd
+camel.springboot.main-run-controller=true
+service.random.url = http://localhost:8080
+#logging.level.org.apache.cxf = TRACE
diff --git a/rest-cxf-opentelemetry/rest-cxf-otel-random/pom.xml b/rest-cxf-opentelemetry/rest-cxf-otel-random/pom.xml
new file mode 100644
index 00000000..ed984b9f
--- /dev/null
+++ b/rest-cxf-opentelemetry/rest-cxf-otel-random/pom.xml
@@ -0,0 +1,67 @@
+
+
+
+
+ 4.0.0
+
+
+ org.apache.camel.springboot.example
+ camel-example-spring-boot-rest-cxf-opentelemetry
+ 4.8.0-SNAPSHOT
+
+
+ camel-example-spring-boot-rest-cxf-otel-random
+ Camel SB Examples :: REST using CXF and OpenTelemetry :: Random
+
+
+ CXF
+
+
+
+
+ org.apache.camel.springboot.example
+ camel-example-spring-boot-rest-cxf-otel-common
+ ${project.version}
+
+
+
+ org.apache.camel.springboot
+ camel-minio-starter
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+ ${spring-boot-version}
+
+
+
+ repackage
+
+
+
+
+
+
+
+
diff --git a/rest-cxf-opentelemetry/rest-cxf-otel-random/src/main/java/org/apache/camel/example/springboot/cxf/otel/Application.java b/rest-cxf-opentelemetry/rest-cxf-otel-random/src/main/java/org/apache/camel/example/springboot/cxf/otel/Application.java
new file mode 100644
index 00000000..15b7d453
--- /dev/null
+++ b/rest-cxf-opentelemetry/rest-cxf-otel-random/src/main/java/org/apache/camel/example/springboot/cxf/otel/Application.java
@@ -0,0 +1,37 @@
+/*
+ * 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.example.springboot.cxf.otel;
+
+import org.apache.camel.opentelemetry.starter.CamelOpenTelemetry;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+// CHECKSTYLE:OFF
+@SpringBootApplication
+@CamelOpenTelemetry
+public class Application {
+
+ /**
+ * Main method to start the application.
+ */
+ public static void main(String[] args) {
+ SpringApplication.run(Application.class, args);
+ }
+
+}
+// CHECKSTYLE:ON
diff --git a/rest-cxf-opentelemetry/rest-cxf-otel-random/src/main/java/org/apache/camel/example/springboot/cxf/otel/CamelRouter.java b/rest-cxf-opentelemetry/rest-cxf-otel-random/src/main/java/org/apache/camel/example/springboot/cxf/otel/CamelRouter.java
new file mode 100644
index 00000000..9d7e9c4f
--- /dev/null
+++ b/rest-cxf-opentelemetry/rest-cxf-otel-random/src/main/java/org/apache/camel/example/springboot/cxf/otel/CamelRouter.java
@@ -0,0 +1,131 @@
+/*
+ * 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.example.springboot.cxf.otel;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.bean.validator.BeanValidationException;
+import org.apache.camel.component.cxf.common.message.CxfConstants;
+import org.apache.camel.component.minio.MinioConstants;
+import org.apache.camel.model.dataformat.JsonLibrary;
+
+import org.springframework.stereotype.Component;
+
+import java.security.SecureRandom;
+
+import io.minio.Result;
+import io.minio.messages.Contents;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+
+@Component
+public class CamelRouter extends RouteBuilder {
+
+
+ @Override
+ public void configure() throws Exception {
+ //very raw way, just to handle the validation responses
+ onException(BeanValidationException.class)
+ .handled(true)
+ .setHeader(Exchange.HTTP_RESPONSE_CODE, constant(Response.Status.BAD_REQUEST.getStatusCode()))
+ .setBody(simple("${exchangeProperty.CamelExceptionCaught.getMessage()}"));
+
+ // @formatter:off
+ from("cxfrs:/api?" +
+ "resourceClasses=org.apache.camel.example.springboot.cxf.otel.RandomService" +
+ "&bindingStyle=SimpleConsumer" +
+ "&providers=jaxrsProvider,openTelemetryProvider" +
+ "&loggingFeatureEnabled=true")
+ .to("log:camel-cxf-log?showAll=true")
+ .setHeader(Exchange.BEAN_METHOD_NAME, simple("${header.operationName}"))
+ .bean(RandomServiceImpl.class);
+
+
+ from("direct:play").routeId("play")
+ .loop(header("attempts"))
+ .process(exchange -> exchange.getIn().getHeaders().clear())
+ .setHeader(CxfConstants.HTTP_METHOD, constant("GET"))
+ .toD("cxfrs:{{service.random.url}}/services/api/generate")
+ .process(exchange -> exchange.getIn().getHeaders().clear())
+ .setHeader(CxfConstants.HTTP_METHOD, constant("POST"))
+ .setHeader(CxfConstants.CONTENT_TYPE, constant(MediaType.APPLICATION_JSON))
+ .toD("cxfrs:{{service.even.url}}/services/api/check")
+ .process(exchange -> exchange.getIn().getHeaders().clear())
+ .setHeader(CxfConstants.HTTP_METHOD, constant("POST"))
+ .setHeader(CxfConstants.CONTENT_TYPE, constant(MediaType.APPLICATION_JSON))
+ .toD("cxfrs:{{service.odd.url}}/services/api/check")
+ .end()
+ .setBody(constant(null));
+
+ from("direct:random").routeId("generate-random")
+ .loadBalance().random()
+ .to("direct:generate-even")
+ .to("direct:generate-odd")
+ .end()
+ .convertBodyTo(RandomNumber.class);
+
+ from("direct:generate-even")
+ .routeId("generate-even")
+ .process(exchange -> {
+ int num = random();
+ while (num % 2 != 0) {
+ num = random();
+ }
+ exchange.getIn().setBody(num);
+ });
+
+ from("direct:generate-odd").routeId("generate-odd")
+ .process(exchange -> {
+ int num = random();
+ while (num % 2 == 0) {
+ num = random();
+ }
+ exchange.getIn().setBody(num);
+ });
+
+ from("direct:save-obj").routeId("save-obj")
+ .setHeader(MinioConstants.OBJECT_NAME, header("objectName"))
+ .marshal().json(JsonLibrary.Jackson)
+ .toD("minio://{{bucket.name}}");
+
+ from("direct:load-results").routeId("load-results")
+ .setVariable("results", () -> new Results())
+ .toD("minio://{{bucket.name}}?operation=listObjects")
+ .split(body())
+ .process(exchange -> {
+ try {
+ exchange.getIn().setHeader(MinioConstants.OBJECT_NAME
+ , ((Contents) exchange.getIn().getBody(Result.class).get()).objectName());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ })
+ .toD("minio://{{bucket.name}}?operation=getObject")
+ .unmarshal().json(JsonLibrary.Jackson, RandomNumber.class)
+ .process(exchange -> exchange.getVariable("results", Results.class)
+ .addNumber(exchange.getIn().getBody(RandomNumber.class)))
+ .end()
+ .setBody(variable("results"));
+ // @formatter:on
+
+ }
+
+ private static int random() {
+ return new SecureRandom().nextInt(100, 1000);
+ }
+
+}
diff --git a/rest-cxf-opentelemetry/rest-cxf-otel-random/src/main/java/org/apache/camel/example/springboot/cxf/otel/RandomService.java b/rest-cxf-opentelemetry/rest-cxf-otel-random/src/main/java/org/apache/camel/example/springboot/cxf/otel/RandomService.java
new file mode 100644
index 00000000..fd2e23b3
--- /dev/null
+++ b/rest-cxf-opentelemetry/rest-cxf-otel-random/src/main/java/org/apache/camel/example/springboot/cxf/otel/RandomService.java
@@ -0,0 +1,71 @@
+/*
+ * 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.example.springboot.cxf.otel;
+
+import org.apache.camel.Exchange;
+import org.apache.cxf.tracing.TracerContext;
+
+import jakarta.validation.Valid;
+import jakarta.ws.rs.Consumes;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.PUT;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.PathParam;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.core.Context;
+import jakarta.ws.rs.core.MediaType;
+
+/**
+ * Service interface for managing users.
+ */
+@Consumes(MediaType.APPLICATION_JSON)
+@Produces(MediaType.APPLICATION_JSON)
+public interface RandomService {
+
+ /**
+ * Find a user by the given ID
+ *
+ * @param attempts
+ * the number of attempts, how many random numbers will be generated
+ * @return Results, the results
+ */
+ @POST
+ @Path("/play/{attempts}")
+ Results play(@PathParam("attempts") Integer attempts);
+
+ /**
+ * Generate random number between 100 and 1000
+ *
+ * @return RandomNumber, the number
+ */
+ @GET
+ @Path("/generate")
+ RandomNumber generate(@Context TracerContext tracer);
+
+ /**
+ * Register result
+ *
+ * @param number
+ * the number to register
+ *
+ */
+ @PUT
+ @Path("/register")
+ void register(@Valid RandomNumber number, Exchange exchange);
+
+}
diff --git a/rest-cxf-opentelemetry/rest-cxf-otel-random/src/main/java/org/apache/camel/example/springboot/cxf/otel/RandomServiceImpl.java b/rest-cxf-opentelemetry/rest-cxf-otel-random/src/main/java/org/apache/camel/example/springboot/cxf/otel/RandomServiceImpl.java
new file mode 100644
index 00000000..1941b0aa
--- /dev/null
+++ b/rest-cxf-opentelemetry/rest-cxf-otel-random/src/main/java/org/apache/camel/example/springboot/cxf/otel/RandomServiceImpl.java
@@ -0,0 +1,59 @@
+/*
+ * 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.example.springboot.cxf.otel;
+
+import static org.apache.camel.example.springboot.cxf.otel.Constants.SERVICE_HEADER_NAME;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.ProducerTemplate;
+import org.apache.cxf.tracing.TracerContext;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+
+public class RandomServiceImpl implements RandomService {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(RandomServiceImpl.class);
+
+ @Autowired
+ ProducerTemplate producerTemplate;
+
+ @Override
+ public Results play(Integer attempts) {
+ producerTemplate.requestBodyAndHeader("direct:play", null, "attempts", attempts, Void.class);
+ return producerTemplate.requestBody("direct:load-results", null, Results.class);
+ }
+
+ @Override
+ public RandomNumber generate(final TracerContext tracer) {
+ tracer.timeline("generate number");
+ tracer.annotate("custom-op", "generate");
+ return producerTemplate.requestBody("direct:random", null, RandomNumber.class);
+ }
+
+ @Override
+ public void register(RandomNumber number, Exchange exchange) {
+ final String serviceName = (String) exchange.getIn().getHeader(SERVICE_HEADER_NAME);
+ assert serviceName != null;
+ assert number != null;
+ assert number.getType() != null;
+ LOGGER.info("register {}", number);
+ final String objName = String.format("%s-%s", serviceName, number.getNumber());
+ producerTemplate.sendBodyAndHeader("direct:save-obj", number, "objectName", objName);
+ }
+}
diff --git a/rest-cxf-opentelemetry/rest-cxf-otel-random/src/main/java/org/apache/camel/example/springboot/cxf/otel/Results.java b/rest-cxf-opentelemetry/rest-cxf-otel-random/src/main/java/org/apache/camel/example/springboot/cxf/otel/Results.java
new file mode 100644
index 00000000..ba0d7e56
--- /dev/null
+++ b/rest-cxf-opentelemetry/rest-cxf-otel-random/src/main/java/org/apache/camel/example/springboot/cxf/otel/Results.java
@@ -0,0 +1,62 @@
+/*
+ * 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.example.springboot.cxf.otel;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+
+import jakarta.validation.constraints.NotNull;
+
+public class Results {
+ @NotNull
+ Map> result;
+
+ public Results() {
+ result = new ConcurrentHashMap<>(2);
+ }
+
+ public Map> getResult() {
+ return result;
+ }
+
+ public int getEvenCount() {
+ return count(Constants.NUM_TYPE.EVEN);
+ }
+
+ public int getOddCount() {
+ return count(Constants.NUM_TYPE.ODD);
+ }
+
+ public Results addNumber(RandomNumber num) {
+ addNumber(num.getType(), num);
+ return this;
+ }
+
+ private void addNumber(Constants.NUM_TYPE evenOddKey, RandomNumber number) {
+ final List values = Optional.ofNullable(result.get(evenOddKey)).orElseGet(() -> new ArrayList<>());
+ values.add(number);
+ result.put(evenOddKey, values);
+ }
+
+ private Integer count(Constants.NUM_TYPE key) {
+ return Optional.ofNullable(result.get(key)).map(List::size).orElse(0);
+ }
+
+}
diff --git a/rest-cxf-opentelemetry/rest-cxf-otel-random/src/main/resources/application.properties b/rest-cxf-opentelemetry/rest-cxf-otel-random/src/main/resources/application.properties
new file mode 100644
index 00000000..dd013fa9
--- /dev/null
+++ b/rest-cxf-opentelemetry/rest-cxf-otel-random/src/main/resources/application.properties
@@ -0,0 +1,28 @@
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+
+# the name of Camel
+camel.springboot.name = cxf-otel-random
+camel.springboot.main-run-controller=true
+service.random.url = http://localhost:8080
+service.even.url = http://localhost:8081
+service.odd.url = http://localhost:8082
+bucket.name = evenodd
+camel.component.minio.access-key = admin
+camel.component.minio.secret-key = admin123
+camel.component.minio.endpoint = http://localhost:9000
+#logging.level.org.apache.cxf = TRACE