From f1fcd42dcd160fece20ed8086087223c1644bb06 Mon Sep 17 00:00:00 2001 From: Marco Carletti Date: Wed, 28 Aug 2024 15:29:10 +0200 Subject: [PATCH] CAMEL-21118: Adds rest-cxf-opentelemetry (#142) --- README.adoc | 4 +- pom.xml | 1 + rest-cxf-opentelemetry/README.adoc | 145 ++++++++++++++++ .../containers/docker-compose.yml | 35 ++++ rest-cxf-opentelemetry/containers/env.sh | 8 + .../containers/otel-collector.yml | 25 +++ rest-cxf-opentelemetry/docs/jaeger.png | Bin 0 -> 35152 bytes rest-cxf-opentelemetry/docs/overview.png | Bin 0 -> 10026 bytes rest-cxf-opentelemetry/pom.xml | 163 ++++++++++++++++++ .../rest-cxf-otel-common/pom.xml | 78 +++++++++ .../cxf/otel/InputStreamConverterLoader.java | 54 ++++++ .../cxf/otel/IntegerConverterLoader.java | 54 ++++++ .../org/apache/camel/TypeConverterLoader | 3 + .../springboot/cxf/otel/Constants.java | 30 ++++ .../springboot/cxf/otel/CxfConfig.java | 39 +++++ .../cxf/otel/InputStreamConverter.java | 34 ++++ .../springboot/cxf/otel/IntegerConverter.java | 29 ++++ .../springboot/cxf/otel/RandomNumber.java | 73 ++++++++ .../rest-cxf-otel-even/pom.xml | 63 +++++++ .../springboot/cxf/otel/Application.java | 37 ++++ .../springboot/cxf/otel/CamelRouter.java | 63 +++++++ .../springboot/cxf/otel/EvenService.java | 43 +++++ .../springboot/cxf/otel/EvenServiceImpl.java | 42 +++++ .../src/main/resources/application.properties | 22 +++ .../rest-cxf-otel-odd/pom.xml | 63 +++++++ .../springboot/cxf/otel/Application.java | 37 ++++ .../springboot/cxf/otel/CamelRouter.java | 63 +++++++ .../springboot/cxf/otel/OddService.java | 43 +++++ .../springboot/cxf/otel/OddServiceImpl.java | 42 +++++ .../src/main/resources/application.properties | 22 +++ .../rest-cxf-otel-random/pom.xml | 67 +++++++ .../springboot/cxf/otel/Application.java | 37 ++++ .../springboot/cxf/otel/CamelRouter.java | 131 ++++++++++++++ .../springboot/cxf/otel/RandomService.java | 71 ++++++++ .../cxf/otel/RandomServiceImpl.java | 59 +++++++ .../example/springboot/cxf/otel/Results.java | 62 +++++++ .../src/main/resources/application.properties | 28 +++ 37 files changed, 1769 insertions(+), 1 deletion(-) create mode 100644 rest-cxf-opentelemetry/README.adoc create mode 100644 rest-cxf-opentelemetry/containers/docker-compose.yml create mode 100644 rest-cxf-opentelemetry/containers/env.sh create mode 100644 rest-cxf-opentelemetry/containers/otel-collector.yml create mode 100644 rest-cxf-opentelemetry/docs/jaeger.png create mode 100644 rest-cxf-opentelemetry/docs/overview.png create mode 100644 rest-cxf-opentelemetry/pom.xml create mode 100644 rest-cxf-opentelemetry/rest-cxf-otel-common/pom.xml create mode 100644 rest-cxf-opentelemetry/rest-cxf-otel-common/src/generated/java/org/apache/camel/example/springboot/cxf/otel/InputStreamConverterLoader.java create mode 100644 rest-cxf-opentelemetry/rest-cxf-otel-common/src/generated/java/org/apache/camel/example/springboot/cxf/otel/IntegerConverterLoader.java create mode 100644 rest-cxf-opentelemetry/rest-cxf-otel-common/src/generated/resources/META-INF/services/org/apache/camel/TypeConverterLoader create mode 100644 rest-cxf-opentelemetry/rest-cxf-otel-common/src/main/java/org/apache/camel/example/springboot/cxf/otel/Constants.java create mode 100644 rest-cxf-opentelemetry/rest-cxf-otel-common/src/main/java/org/apache/camel/example/springboot/cxf/otel/CxfConfig.java create mode 100644 rest-cxf-opentelemetry/rest-cxf-otel-common/src/main/java/org/apache/camel/example/springboot/cxf/otel/InputStreamConverter.java create mode 100644 rest-cxf-opentelemetry/rest-cxf-otel-common/src/main/java/org/apache/camel/example/springboot/cxf/otel/IntegerConverter.java create mode 100644 rest-cxf-opentelemetry/rest-cxf-otel-common/src/main/java/org/apache/camel/example/springboot/cxf/otel/RandomNumber.java create mode 100644 rest-cxf-opentelemetry/rest-cxf-otel-even/pom.xml create mode 100644 rest-cxf-opentelemetry/rest-cxf-otel-even/src/main/java/org/apache/camel/example/springboot/cxf/otel/Application.java create mode 100644 rest-cxf-opentelemetry/rest-cxf-otel-even/src/main/java/org/apache/camel/example/springboot/cxf/otel/CamelRouter.java create mode 100644 rest-cxf-opentelemetry/rest-cxf-otel-even/src/main/java/org/apache/camel/example/springboot/cxf/otel/EvenService.java create mode 100644 rest-cxf-opentelemetry/rest-cxf-otel-even/src/main/java/org/apache/camel/example/springboot/cxf/otel/EvenServiceImpl.java create mode 100644 rest-cxf-opentelemetry/rest-cxf-otel-even/src/main/resources/application.properties create mode 100644 rest-cxf-opentelemetry/rest-cxf-otel-odd/pom.xml create mode 100644 rest-cxf-opentelemetry/rest-cxf-otel-odd/src/main/java/org/apache/camel/example/springboot/cxf/otel/Application.java create mode 100644 rest-cxf-opentelemetry/rest-cxf-otel-odd/src/main/java/org/apache/camel/example/springboot/cxf/otel/CamelRouter.java create mode 100644 rest-cxf-opentelemetry/rest-cxf-otel-odd/src/main/java/org/apache/camel/example/springboot/cxf/otel/OddService.java create mode 100644 rest-cxf-opentelemetry/rest-cxf-otel-odd/src/main/java/org/apache/camel/example/springboot/cxf/otel/OddServiceImpl.java create mode 100644 rest-cxf-opentelemetry/rest-cxf-otel-odd/src/main/resources/application.properties create mode 100644 rest-cxf-opentelemetry/rest-cxf-otel-random/pom.xml create mode 100644 rest-cxf-opentelemetry/rest-cxf-otel-random/src/main/java/org/apache/camel/example/springboot/cxf/otel/Application.java create mode 100644 rest-cxf-opentelemetry/rest-cxf-otel-random/src/main/java/org/apache/camel/example/springboot/cxf/otel/CamelRouter.java create mode 100644 rest-cxf-opentelemetry/rest-cxf-otel-random/src/main/java/org/apache/camel/example/springboot/cxf/otel/RandomService.java create mode 100644 rest-cxf-opentelemetry/rest-cxf-otel-random/src/main/java/org/apache/camel/example/springboot/cxf/otel/RandomServiceImpl.java create mode 100644 rest-cxf-opentelemetry/rest-cxf-otel-random/src/main/java/org/apache/camel/example/springboot/cxf/otel/Results.java create mode 100644 rest-cxf-opentelemetry/rest-cxf-otel-random/src/main/resources/application.properties 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-streams resilience4j rest-cxf + rest-cxf-opentelemetry rest-openapi rest-openapi-simple rest-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 0000000000000000000000000000000000000000..e89e987dd980b984c0fb3e4c99d8214bbdea6ac9 GIT binary patch literal 35152 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�Rn)ek&qIlZ07W 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

bmmhvXorFa z6Ykc+7tzm1puGm14Ho4Ue%=`71dKUTpCDtakN!rQm%`ofVuCE6>33rAwqxFlzhXaI ztA@$eNo$#i^OSU^Qze7YdiNWYyZvJhR8(pA!RXmJfHfh5#4`F@m=Gw6Pqhsj;lTi6 zmsVO4f|5JlRAyU8PjIJtMA+VoyUWyPJq7s%oaC0j_Y2OdM=_2_arnZKS9!Gw!9N!Q z{`>$Op5$-oBEL6rPUyK(XQ~O08iwA4M*lSEsv{FP8jjC+W7br?v8m725P_venv}^L zzn|~aER)#B=4vFkR%9zu*I$(rwUt>Ub;x#S#7_6AVSU;QQEH9}}h2LT~@$KeMug za#)qe8!w+MUsON6JVaL=AC8Kiau! zbilA4S!wsp|37WmN;Nn4K%3Z1;Bn}}MAl$a^o=C-Kks${L!4*Ub5~PPS+uNHZ2-2B zHdl{X9<$xbEXf9Skn@hJKukpMl7}LL>c`Q^C%+=PZAf4B;T4AOGGvWenlq`X`hd!c zM>3QOoveFt8ir0&I{9t)#dz9cmO`AoqAM$2k`1@!R=2;v6k#GXh(-2UM5$aKX;O1Gj#@_tKLDf;-Fz%hrv z0?HRGgzVh$a7(0MQVfa@hwCpN_Q13aCM1opX%a;i6%{o(pP>3!E{^-)7@7-U&;S(} z0dBRC#uuWEgh;-V;-*W1iYI^+-u9g<#~>_&_8-)YN{rx?e*+pwN1Ar{L5>lawkbD+ z(PwnEo&9iyTb`sda4ztl*v_#UeOcJt0m*(Dvu5$(auKQ26(g!wKI@sj8VRG9Eo6m* z=0Cvp41Q5obq`Z_>dG2qx>w0b^$l*{z6Lx1i~6V7tW(Rnanx&gqqIhNTtzF z-@u?6rqVfQJNH2P#mC2ov<_*L{paW2gGKv|y>5!rrJ8bV=;Tr@lN>fqcl20cZog<( z1)+;4ne~t1Jv6!*AYu_lK$ukjz4ynJ?SFpBxc_rC1e2nA&_n-DgIKkG8XPza=D#ir zh5!A^nE!KCvi?UGLWb(O^uL!0>3{YK@F{Qv=6_!Txc_++;Ztt@&(%&90yhDECbGDP zvhXRa|8p%aBP%t@9xW*Rr}u!*BK@Ds&35d-MMa`L{rb85eXpPU*25>x7;tH*=qgt# H+64a}+cTw= literal 0 HcmV?d00001 diff --git a/rest-cxf-opentelemetry/docs/overview.png b/rest-cxf-opentelemetry/docs/overview.png new file mode 100644 index 0000000000000000000000000000000000000000..15550cd62149221e41373038a1317cbc9dad3dc5 GIT binary patch literal 10026 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 Uusa*@4jkyNx~^L3ZM&!c2KsSbs{jB1 literal 0 HcmV?d00001 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/java + + + + 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