Wrap AsyncWebClient to use less the ApplicationContextProvider 08/138308/9 master
authorlapentafd <francesco.lapenta@est.tech>
Mon, 24 Jun 2024 09:54:54 +0000 (10:54 +0100)
committerlapentafd <francesco.lapenta@est.tech>
Wed, 26 Jun 2024 13:41:42 +0000 (14:41 +0100)
Issue-ID: CCSDK-4022
Change-Id: I2ecc3431ef646fe408de0de35698e8f369a55ee4
Signed-off-by: lapentafd <francesco.lapenta@est.tech>
a1-policy-management/config/application.yaml
a1-policy-management/pom.xml
a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/clients/AsyncRestClient.java
a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/configuration/ApplicationContextProvider.java [deleted file]
a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/configuration/OtelConfig.java
a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/configuration/WebClientUtil.java [new file with mode: 0644]
a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/authorization/AuthorizationCheck.java
a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/configuration/OtelConfigTest.java
a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/controllers/v2/ApplicationTest.java

index 52b70d1..39b4486 100644 (file)
@@ -29,18 +29,7 @@ spring:
   aop:
     auto: false
 management:
-  otlp:
-    metrics:
-      export:
-        enabled: false
   tracing:
-    sampler:
-      jaeger_remote:
-        endpoint: ${ONAP_OTEL_SAMPLER_JAEGER_REMOTE_ENDPOINT:http://jaeger:14250}
-    exporter:
-      endpoint: ${ONAP_OTEL_EXPORTER_ENDPOINT:http://jaeger:4317}
-      protocol: ${ONAP_OTEL_EXPORTER_PROTOCOL:grpc}
-    enabled: ${ONAP_TRACING_ENABLED:false}
     propagation:
       produce: ${ONAP_PROPAGATOR_PRODUCE:[W3C]}
     sampling:
@@ -119,7 +108,19 @@ app:
     secretAccessKey: miniostorage
     bucket:
 otel:
+  sdk:
+    disabled: ${ONAP_SDK_DISABLED:true}
+    south: ${ONAP_TRACING_SOUTHBOUND:true}
+  tracing:
+    sampler:
+      jaeger_remote:
+        endpoint: ${ONAP_OTEL_SAMPLER_JAEGER_REMOTE_ENDPOINT:http://jaeger:14250}
   exporter:
     otlp:
       traces:
-        protocol: ${ONAP_OTEL_EXPORTER_OTLP_TRACES_PROTOCOL:grpc}
+        protocol: ${ONAP_OTEL_EXPORTER_PROTOCOL:grpc}
+        endpoint: ${ONAP_OTEL_EXPORTER_ENDPOINT:http://jaeger:4317}
+  metrics:
+    exporter: none
+  logs:
+    exporter: none
index c383ba2..0d53b90 100644 (file)
             <groupId>io.micrometer</groupId>
             <artifactId>micrometer-tracing-bridge-otel</artifactId>
         </dependency>
-        <dependency>
-            <groupId>io.opentelemetry</groupId>
-            <artifactId>opentelemetry-exporter-otlp</artifactId>
-        </dependency>
         <dependency>
             <groupId>io.opentelemetry</groupId>
             <artifactId>opentelemetry-sdk-extension-jaeger-remote-sampler</artifactId>
         </dependency>
-        <dependency>
-            <groupId>io.opentelemetry</groupId>
-            <artifactId>opentelemetry-sdk-extension-autoconfigure</artifactId>
-        </dependency>
         <dependency>
             <groupId>io.opentelemetry.instrumentation</groupId>
             <artifactId>opentelemetry-spring-webflux-5.3</artifactId>
         </dependency>
         <dependency>
-            <groupId>io.micrometer</groupId>
-            <artifactId>context-propagation</artifactId>
-            <version>1.0.2</version>
+            <groupId>io.opentelemetry.instrumentation</groupId>
+            <artifactId>opentelemetry-spring-boot-starter</artifactId>
         </dependency>
         <!-- For ObservationRegistryCustomizer -->
         <dependency>
index ddcfd06..476c160 100644 (file)
@@ -25,22 +25,16 @@ import io.netty.channel.ChannelOption;
 import io.netty.handler.ssl.SslContext;
 import io.netty.handler.timeout.ReadTimeoutHandler;
 import io.netty.handler.timeout.WriteTimeoutHandler;
-import io.opentelemetry.instrumentation.spring.webflux.v5_3.SpringWebfluxTelemetry;
 
 import java.lang.invoke.MethodHandles;
-import java.util.concurrent.atomic.AtomicInteger;
 
-import org.onap.ccsdk.oran.a1policymanagementservice.configuration.ApplicationContextProvider;
-import org.onap.ccsdk.oran.a1policymanagementservice.configuration.OtelConfig;
 import org.onap.ccsdk.oran.a1policymanagementservice.configuration.WebClientConfig.HttpProxyConfig;
+import org.onap.ccsdk.oran.a1policymanagementservice.configuration.WebClientUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.http.MediaType;
 import org.springframework.http.ResponseEntity;
-import org.springframework.http.client.reactive.ReactorClientHttpConnector;
 import org.springframework.lang.Nullable;
-import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
-import org.springframework.web.reactive.function.client.ExchangeStrategies;
 import org.springframework.web.reactive.function.client.WebClient;
 import org.springframework.web.reactive.function.client.WebClient.RequestHeadersSpec;
 import org.springframework.web.reactive.function.client.WebClientResponseException;
@@ -57,11 +51,9 @@ public class AsyncRestClient {
     private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
     private WebClient webClient = null;
     private final String baseUrl;
-    private static final AtomicInteger sequenceNumber = new AtomicInteger();
     private final SslContext sslContext;
     private final HttpProxyConfig httpProxyConfig;
     private final SecurityContext securityContext;
-    private OtelConfig otelConfig = ApplicationContextProvider.getApplicationContext().getBean(OtelConfig.class);
 
     public AsyncRestClient(String baseUrl, @Nullable SslContext sslContext, @Nullable HttpProxyConfig httpProxyConfig,
             SecurityContext securityContext) {
@@ -156,10 +148,6 @@ public class AsyncRestClient {
         }
     }
 
-    private static Object createTraceTag() {
-        return sequenceNumber.incrementAndGet();
-    }
-
     private String toBody(ResponseEntity<String> entity) {
         if (entity.getBody() == null) {
             return "";
@@ -193,36 +181,8 @@ public class AsyncRestClient {
     }
 
     public WebClient buildWebClient(String baseUrl) {
-        Object traceTag = createTraceTag();
-
         final HttpClient httpClient = buildHttpClient();
-        ExchangeStrategies exchangeStrategies = ExchangeStrategies.builder() //
-                .codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(-1)) //
-                .build();
-
-        ExchangeFilterFunction reqLogger = ExchangeFilterFunction.ofRequestProcessor(req -> {
-            logger.debug("{} {} uri = '{}''", traceTag, req.method(), req.url());
-            return Mono.just(req);
-        });
-
-        ExchangeFilterFunction respLogger = ExchangeFilterFunction.ofResponseProcessor(resp -> {
-            logger.debug("{} resp: {}", traceTag, resp.statusCode());
-            return Mono.just(resp);
-        });
-
-        WebClient.Builder webClientBuilder = WebClient.builder()
-                .clientConnector(new ReactorClientHttpConnector(httpClient))
-                .baseUrl(baseUrl)
-                .exchangeStrategies(exchangeStrategies)
-                .filter(reqLogger)
-                .filter(respLogger);
-
-        if (otelConfig.isTracingEnabled()) {
-            SpringWebfluxTelemetry webfluxTelemetry = ApplicationContextProvider.getApplicationContext().getBean(SpringWebfluxTelemetry.class);
-            webClientBuilder.filters(webfluxTelemetry::addClientTracingFilter);
-        }
-
-        return webClientBuilder.build();
+        return WebClientUtil.buildWebClient(baseUrl, httpClient);
     }
 
     private WebClient getWebClient() {
diff --git a/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/configuration/ApplicationContextProvider.java b/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/configuration/ApplicationContextProvider.java
deleted file mode 100644 (file)
index a713020..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-/*-
- * ========================LICENSE_START=================================
- * ONAP : ccsdk oran
- * ======================================================================
- * Copyright (C) 2024 OpenInfra Foundation Europe. All rights reserved.
- * ======================================================================
- * Licensed 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.
- * ========================LICENSE_END===================================
- */
-package org.onap.ccsdk.oran.a1policymanagementservice.configuration;
-
-import org.springframework.beans.BeansException;
-import org.springframework.context.ApplicationContext;
-import org.springframework.context.ApplicationContextAware;
-import org.springframework.context.annotation.Configuration;
-
-@Configuration
-public class ApplicationContextProvider implements ApplicationContextAware {
-    private static ApplicationContext context;
-
-    @Override
-    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
-        context = applicationContext;
-    }
-
-    public static ApplicationContext getApplicationContext() {
-        return context;
-    }
-}
\ No newline at end of file
index a66bc06..cac0320 100644 (file)
@@ -27,11 +27,8 @@ import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter;
 import io.opentelemetry.instrumentation.spring.webflux.v5_3.SpringWebfluxTelemetry;
 import io.opentelemetry.sdk.extension.trace.jaeger.sampler.JaegerRemoteSampler;
 import io.opentelemetry.sdk.trace.samplers.Sampler;
-import lombok.Getter;
-import reactor.core.publisher.Hooks;
 
 import java.time.Duration;
-
 import javax.annotation.PostConstruct;
 
 import org.slf4j.Logger;
@@ -57,40 +54,50 @@ public class OtelConfig {
     @Value("${spring.application.name}")
     private String serviceId;
 
-    @Value("${management.tracing.exporter.endpoint}")
+    @Value("${otel.exporter.otlp.traces.endpoint}")
     private String tracingExporterEndpointUrl;
 
-    @Value("${management.tracing.sampler.jaeger-remote.endpoint}")
+    @Value("${otel.tracing.sampler.jaeger-remote.endpoint}")
     private String jaegerRemoteSamplerUrl;
 
-    @Value("${management.tracing.exporter.protocol}")
+    @Value("${otel.exporter.otlp.traces.protocol}")
     private String tracingProtocol;
 
-    @Getter
-    @Value("${management.tracing.enabled}")
-    private boolean tracingEnabled;
+    @Value("${otel.sdk.disabled}")
+    private boolean tracingDisabled;
+
+    @Value("${otel.sdk.south}")
+    private boolean southTracingEnabled;
 
     @PostConstruct
     public void checkTracingConfig() {
-        logger.info("Application Yaml Tracing Enabled: " + tracingEnabled);
+        logger.info("Application Yaml Tracing Enabled: " + !tracingDisabled);
+    }
+
+    public boolean isTracingEnabled() {
+        return !tracingDisabled;
+    }
+
+    public boolean isSouthTracingEnabled() {
+        return isTracingEnabled() && southTracingEnabled;
     }
 
     @Bean
-    @ConditionalOnProperty(prefix = "management.tracing", name = "enabled", havingValue = "true", matchIfMissing = false)
-    @ConditionalOnExpression("'grpc'.equals('${management.tracing.exporter.protocol}')")
+    @ConditionalOnProperty(prefix = "otel.sdk", name = "disabled", havingValue = "false", matchIfMissing = false)
+    @ConditionalOnExpression("'grpc'.equals('${otel.exporter.otlp.traces.protocol}')")
     public OtlpGrpcSpanExporter otlpExporterGrpc() {
         return OtlpGrpcSpanExporter.builder().setEndpoint(tracingExporterEndpointUrl).build();
     }
 
     @Bean
-    @ConditionalOnProperty(prefix = "management.tracing", name = "enabled", havingValue = "true", matchIfMissing = false)
-    @ConditionalOnExpression("'http'.equals('${management.tracing.exporter.protocol}')")
+    @ConditionalOnProperty(prefix = "otel.sdk", name = "disabled", havingValue = "false", matchIfMissing = false)
+    @ConditionalOnExpression("'http'.equals('${otel.exporter.otlp.traces.protocol}')")
     public OtlpHttpSpanExporter otlpExporterHttp() {
         return OtlpHttpSpanExporter.builder().setEndpoint(tracingExporterEndpointUrl).build();
     }
 
     @Bean
-    @ConditionalOnProperty(prefix = "management.tracing", name = "enabled", havingValue = "true", matchIfMissing = false)
+    @ConditionalOnProperty(prefix = "otel.sdk", name = "disabled", havingValue = "false", matchIfMissing = false)
     public JaegerRemoteSampler jaegerRemoteSampler() {
         return JaegerRemoteSampler.builder().setEndpoint(jaegerRemoteSamplerUrl)
                 .setPollingInterval(Duration.ofSeconds(JAEGER_REMOTE_SAMPLER_POLLING_INTERVAL_IN_SECOND))
@@ -98,15 +105,13 @@ public class OtelConfig {
     }
 
     @Bean
-    @ConditionalOnProperty(prefix = "management.tracing", name = "enabled", havingValue = "true", matchIfMissing = false)
+    @ConditionalOnExpression("!${otel.sdk.disabled:true} and ${otel.sdk.south:true}")
     public SpringWebfluxTelemetry webfluxTelemetry (OpenTelemetry openTelemetry) {
-        //enables automatic context propagation to ThreadLocals used by FLUX and MONO operators
-        Hooks.enableAutomaticContextPropagation();
         return SpringWebfluxTelemetry.builder(openTelemetry).build();
     }
 
     @Bean
-    @ConditionalOnProperty(prefix = "management.tracing", name = "enabled", havingValue = "true", matchIfMissing = false)
+    @ConditionalOnProperty(prefix = "otel.sdk", name = "disabled", havingValue = "false", matchIfMissing = false)
     ObservationRegistryCustomizer<ObservationRegistry> skipActuatorEndpointsFromObservation() {
         PathMatcher pathMatcher = new AntPathMatcher("/");
         return registry ->
diff --git a/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/configuration/WebClientUtil.java b/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/configuration/WebClientUtil.java
new file mode 100644 (file)
index 0000000..68d9ea7
--- /dev/null
@@ -0,0 +1,87 @@
+/*-
+ * ========================LICENSE_START=================================
+ * ONAP : ccsdk oran
+ * ======================================================================
+ * Copyright (C) 2024 OpenInfra Foundation Europe. All rights reserved.
+ * ======================================================================
+ * Licensed 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.
+ * ========================LICENSE_END===================================
+ */
+package org.onap.ccsdk.oran.a1policymanagementservice.configuration;
+
+import io.opentelemetry.instrumentation.spring.webflux.v5_3.SpringWebfluxTelemetry;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.DependsOn;
+import org.springframework.http.client.reactive.ReactorClientHttpConnector;
+import org.springframework.stereotype.Service;
+import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
+import org.springframework.web.reactive.function.client.ExchangeStrategies;
+import org.springframework.web.reactive.function.client.WebClient;
+
+import reactor.core.publisher.Mono;
+import reactor.netty.http.client.HttpClient;
+
+import java.lang.invoke.MethodHandles;
+import java.util.concurrent.atomic.AtomicInteger;
+
+@Service
+@DependsOn({"otelConfig"})
+public class WebClientUtil {
+
+    private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+    private static OtelConfig otelConfig;
+
+    private static SpringWebfluxTelemetry springWebfluxTelemetry;
+
+    public WebClientUtil(OtelConfig otelConfig, @Autowired(required = false) SpringWebfluxTelemetry springWebfluxTelemetry) {
+        WebClientUtil.otelConfig = otelConfig;
+        if (otelConfig.isTracingEnabled()) {
+            WebClientUtil.springWebfluxTelemetry = springWebfluxTelemetry;
+        }
+    }
+
+    public static WebClient buildWebClient(String baseURL, final HttpClient httpClient) {
+
+        Object traceTag = new AtomicInteger().incrementAndGet();
+
+        ExchangeStrategies exchangeStrategies = ExchangeStrategies.builder() //
+                .codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(-1)) //
+                .build();
+
+        ExchangeFilterFunction reqLogger = ExchangeFilterFunction.ofRequestProcessor(req -> {
+            logger.debug("{} {} uri = '{}''", traceTag, req.method(), req.url());
+            return Mono.just(req);
+        });
+
+        ExchangeFilterFunction respLogger = ExchangeFilterFunction.ofResponseProcessor(resp -> {
+            logger.debug("{} resp: {}", traceTag, resp.statusCode());
+            return Mono.just(resp);
+        });
+
+        WebClient.Builder webClientBuilder = WebClient.builder()
+                .clientConnector(new ReactorClientHttpConnector(httpClient))
+                .baseUrl(baseURL)
+                .exchangeStrategies(exchangeStrategies)
+                .filter(reqLogger)
+                .filter(respLogger);
+
+        if (otelConfig.isSouthTracingEnabled()) {
+            webClientBuilder.filters(springWebfluxTelemetry::addClientTracingFilter);
+        }
+
+        return webClientBuilder.build();
+    }
+}
index 5ad5068..0f376c0 100644 (file)
@@ -36,14 +36,12 @@ import org.onap.ccsdk.oran.a1policymanagementservice.repository.Policy;
 import org.onap.ccsdk.oran.a1policymanagementservice.repository.PolicyType;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.context.annotation.DependsOn;
 import org.springframework.http.HttpStatus;
 import org.springframework.stereotype.Component;
 
 import reactor.core.publisher.Mono;
 
 @Component
-@DependsOn("applicationContextProvider")
 public class AuthorizationCheck {
 
     private final ApplicationConfig applicationConfig;
index 7c04de2..bd01119 100644 (file)
@@ -28,9 +28,7 @@ import static org.mockito.Mockito.when;
 
 import io.micrometer.observation.Observation;
 import io.micrometer.observation.ObservationRegistry;
-import io.opentelemetry.api.OpenTelemetry;
 import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter;
-import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk;
 import jakarta.servlet.http.HttpServletRequest;
 import java.util.Objects;
 import org.junit.jupiter.api.Test;
@@ -40,8 +38,8 @@ import org.springframework.boot.test.autoconfigure.actuate.observability.AutoCon
 import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
 import org.springframework.context.ApplicationContext;
-import org.springframework.context.annotation.Bean;
 import org.springframework.http.server.observation.ServerRequestObservationContext;
+import org.springframework.test.annotation.DirtiesContext;
 import org.springframework.test.context.TestPropertySource;
 import org.springframework.util.AntPathMatcher;
 
@@ -54,12 +52,14 @@ import org.springframework.util.AntPathMatcher;
     "app.filepath=",
     "app.s3.bucket=",
     "spring.application.name=a1-pms",
-    "management.tracing.enabled=true",
-    "management.tracing.exporter.protocol=grpc",
-    "management.tracing.sampler.jaeger_remote.endpoint=http://127.0.0.1:14250",
+    "otel.sdk.disabled=false",
+    "otel.sdk.south=true",
+    "otel.exporter.otlp.traces.protocol=grpc",
+    "otel.tracing.sampler.jaeger-remote.endpoint=http://127.0.0.1:14250",
     "management.tracing.propagator.type=W3C"
 })
 @AutoConfigureObservability
+@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
 class OtelConfigTest {
 
     @Autowired private ApplicationContext context;
@@ -68,11 +68,6 @@ class OtelConfigTest {
 
     @Autowired ObservationRegistry observationRegistry;
 
-    @Bean
-    OpenTelemetry openTelemetry() {
-        return AutoConfiguredOpenTelemetrySdk.initialize().getOpenTelemetrySdk();
-    }
-
     @Test
     void otlpExporterGrpc() {
         assertNotNull(otelConfig);
index 4f3fa5f..69ee12b 100644 (file)
@@ -195,6 +195,11 @@ class ApplicationTest {
     @BeforeEach
     void init() {
         this.applicationConfig.setAuthProviderUrl(baseUrl() + OpenPolicyAgentSimulatorController.ACCESS_CONTROL_URL);
+        rics.clear();
+        policies.clear();
+        policyTypes.clear();
+        services.clear();
+        a1ClientFactory.reset();
     }
 
     @AfterEach