Skip to content

Duplicate Spring Boot 3 files for Spring Boot 4 #14415

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,20 @@ sourceSets {
setSrcDirs(listOf("src/main/javaSpring3"))
}
}
create("javaSpring4") {
java {
setSrcDirs(listOf("src/main/javaSpring4"))
}
}
}

configurations {
named("javaSpring3CompileOnly") {
extendsFrom(configurations["compileOnly"])
}
named("javaSpring4CompileOnly") {
extendsFrom(configurations["compileOnly"])
}
}

dependencies {
Expand Down Expand Up @@ -106,6 +114,14 @@ dependencies {
add("javaSpring3CompileOnly", "io.opentelemetry:opentelemetry-sdk-extension-autoconfigure")
add("javaSpring3CompileOnly", project(":instrumentation:spring:spring-web:spring-web-3.1:library"))
add("javaSpring3CompileOnly", project(":instrumentation:spring:spring-webmvc:spring-webmvc-6.0:library"))

// give access to common classes, e.g. InstrumentationConfigUtil
add("javaSpring4CompileOnly", files(sourceSets.main.get().output.classesDirs))
add("javaSpring4CompileOnly", "org.springframework.boot:spring-boot-starter-web:4.0.0-M1")
add("javaSpring4CompileOnly", "org.springframework.boot:spring-boot-starter-restclient:4.0.0-M1")
add("javaSpring4CompileOnly", "io.opentelemetry:opentelemetry-sdk-extension-autoconfigure")
add("javaSpring4CompileOnly", project(":instrumentation:spring:spring-web:spring-web-3.1:library"))
add("javaSpring4CompileOnly", project(":instrumentation:spring:spring-webmvc:spring-webmvc-6.0:library"))
}

val latestDepTest = findProperty("testLatestDeps") as Boolean
Expand All @@ -119,6 +135,7 @@ if (latestDepTest) {

val testJavaVersion = gradle.startParameter.projectProperties["testJavaVersion"]?.let(JavaVersion::toVersion)
val testSpring3 = (testJavaVersion == null || testJavaVersion.compareTo(JavaVersion.VERSION_17) >= 0)
val testSpring4 = (testJavaVersion == null || testJavaVersion.compareTo(JavaVersion.VERSION_17) >= 0)

testing {
suites {
Expand Down Expand Up @@ -173,6 +190,20 @@ testing {
}
}
}

val testSpring4 by registering(JvmTestSuite::class) {
dependencies {
implementation(project())
implementation("org.springframework.boot:spring-boot-starter-web:4.0.0-M1")
implementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure")
implementation(project(":instrumentation:spring:spring-web:spring-web-3.1:library"))
implementation(project(":instrumentation:spring:spring-webmvc:spring-webmvc-6.0:library"))
implementation("jakarta.servlet:jakarta.servlet-api:5.0.0")
implementation("org.springframework.boot:spring-boot-starter-test:4.0.0-M1") {
exclude("org.junit.vintage", "junit-vintage-engine")
}
}
}
}
}

Expand Down Expand Up @@ -219,6 +250,30 @@ tasks {
from(sourceSets["javaSpring3"].java)
}

named<JavaCompile>("compileJavaSpring4Java") {
sourceCompatibility = "17"
targetCompatibility = "17"
options.release.set(17)
}

named<JavaCompile>("compileTestSpring4Java") {
sourceCompatibility = "17"
targetCompatibility = "17"
options.release.set(17)
}

named<Test>("testSpring4") {
isEnabled = testSpring4
}

named<Jar>("jar") {
from(sourceSets["javaSpring4"].output)
}

named<Jar>("sourcesJar") {
from(sourceSets["javaSpring4"].java)
}

val testStableSemconv by registering(Test::class) {
jvmArgs("-Dotel.semconv-stability.opt-in=database")
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.spring.autoconfigure.v4_0.internal.instrumentation.annotations;

import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.aot.hint.TypeReference;

class OpenTelemetryAnnotationsRuntimeHints implements RuntimeHintsRegistrar {

@Override
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
hints
.reflection()
.registerType(
TypeReference.of(
"io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.annotations.InstrumentationWithSpanAspect"),
hint -> hint.withMembers(MemberCategory.INVOKE_PUBLIC_METHODS))
.registerType(
TypeReference.of(
"io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.scheduling.SpringSchedulingInstrumentationAspect"),
hint -> hint.withMembers(MemberCategory.INVOKE_PUBLIC_METHODS));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.spring.autoconfigure.v4_0.internal.instrumentation.runtimemetrics;

import io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.runtimemetrics.Java17RuntimeMetricsProvider;
import org.springframework.beans.factory.aot.BeanRegistrationExcludeFilter;
import org.springframework.beans.factory.support.RegisteredBean;

/**
* Configures runtime metrics collection for Java 17+.
*
* <p>This class is internal and is hence not for public use. Its APIs are unstable and can change
* at any time.
*/
public class RuntimeMetricsBeanRegistrationExcludeFilter implements BeanRegistrationExcludeFilter {
@Override
public boolean isExcludedFromAotProcessing(RegisteredBean registeredBean) {
// The JFR-based runtime metric code is excluded from the Spring AOT processing step.
// That way, this code is not included in a Spring native image application.

return Java17RuntimeMetricsProvider.class.getName().equals(registeredBean.getBeanName());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.spring.autoconfigure.v4_0.internal.instrumentation.web;

import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.InstrumentationConfigUtil;
import io.opentelemetry.instrumentation.spring.web.v3_1.SpringWebTelemetry;
import io.opentelemetry.instrumentation.spring.web.v3_1.internal.WebTelemetryUtil;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.web.client.RestClient;

final class RestClientBeanPostProcessor implements BeanPostProcessor {

private final ObjectProvider<OpenTelemetry> openTelemetryProvider;
private final ObjectProvider<ConfigProperties> configPropertiesProvider;

public RestClientBeanPostProcessor(
ObjectProvider<OpenTelemetry> openTelemetryProvider,
ObjectProvider<ConfigProperties> configPropertiesProvider) {
this.openTelemetryProvider = openTelemetryProvider;
this.configPropertiesProvider = configPropertiesProvider;
}

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (bean instanceof RestClient restClient) {
return addRestClientInterceptorIfNotPresent(
restClient, openTelemetryProvider.getObject(), configPropertiesProvider.getObject());
}
return bean;
}

private static RestClient addRestClientInterceptorIfNotPresent(
RestClient restClient, OpenTelemetry openTelemetry, ConfigProperties config) {
ClientHttpRequestInterceptor instrumentationInterceptor = getInterceptor(openTelemetry, config);

return restClient
.mutate()
.requestInterceptors(
interceptors -> {
if (interceptors.stream()
.noneMatch(
interceptor ->
interceptor.getClass() == instrumentationInterceptor.getClass())) {
interceptors.add(0, instrumentationInterceptor);
}
})
.build();
}

static ClientHttpRequestInterceptor getInterceptor(
OpenTelemetry openTelemetry, ConfigProperties config) {
return InstrumentationConfigUtil.configureClientBuilder(
config,
SpringWebTelemetry.builder(openTelemetry),
WebTelemetryUtil.getBuilderExtractor())
.build()
.newInterceptor();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.spring.autoconfigure.v4_0.internal.instrumentation.web;

import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.instrumentation.spring.autoconfigure.internal.ConditionalOnEnabledInstrumentation;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.restclient.RestClientCustomizer;
import org.springframework.boot.restclient.autoconfigure.RestClientAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestClient;

/**
* Configures {@link RestClient} for tracing.
*
* <p>Adds OpenTelemetry instrumentation to {@link RestClient} beans after initialization.
*
* <p>This class is internal and is hence not for public use. Its APIs are unstable and can change
* at any time.
*/
@ConditionalOnEnabledInstrumentation(module = "spring-web")
@ConditionalOnClass(RestClient.class)
@AutoConfiguration(after = RestClientAutoConfiguration.class)
@Configuration
public class RestClientInstrumentationAutoConfiguration {

@Bean
static RestClientBeanPostProcessor otelRestClientBeanPostProcessor(
ObjectProvider<OpenTelemetry> openTelemetryProvider,
ObjectProvider<ConfigProperties> configPropertiesProvider) {
return new RestClientBeanPostProcessor(openTelemetryProvider, configPropertiesProvider);
}

@Bean
RestClientCustomizer otelRestClientCustomizer(
ObjectProvider<OpenTelemetry> openTelemetryProvider,
ObjectProvider<ConfigProperties> configPropertiesProvider) {
return builder ->
builder.requestInterceptor(
RestClientBeanPostProcessor.getInterceptor(
openTelemetryProvider.getObject(), configPropertiesProvider.getObject()));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.spring.autoconfigure.v4_0.internal.instrumentation.web;

import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.web.client.RestTemplate;

final class RestTemplateBeanPostProcessor implements BeanPostProcessor {

private final ObjectProvider<OpenTelemetry> openTelemetryProvider;

private final ObjectProvider<ConfigProperties> configPropertiesProvider;

RestTemplateBeanPostProcessor(
ObjectProvider<OpenTelemetry> openTelemetryProvider,
ObjectProvider<ConfigProperties> configPropertiesProvider) {
this.openTelemetryProvider = openTelemetryProvider;
this.configPropertiesProvider = configPropertiesProvider;
}

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (!(bean instanceof RestTemplate restTemplate)) {
return bean;
}

return RestTemplateInstrumentation.addIfNotPresent(
restTemplate, openTelemetryProvider.getObject(), configPropertiesProvider.getObject());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.spring.autoconfigure.v4_0.internal.instrumentation.web;

import com.google.errorprone.annotations.CanIgnoreReturnValue;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.InstrumentationConfigUtil;
import io.opentelemetry.instrumentation.spring.web.v3_1.SpringWebTelemetry;
import io.opentelemetry.instrumentation.spring.web.v3_1.internal.WebTelemetryUtil;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import java.util.List;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.web.client.RestTemplate;

class RestTemplateInstrumentation {

private RestTemplateInstrumentation() {}

@CanIgnoreReturnValue
static RestTemplate addIfNotPresent(
RestTemplate restTemplate, OpenTelemetry openTelemetry, ConfigProperties config) {

ClientHttpRequestInterceptor instrumentationInterceptor =
InstrumentationConfigUtil.configureClientBuilder(
config,
SpringWebTelemetry.builder(openTelemetry),
WebTelemetryUtil.getBuilderExtractor())
.build()
.newInterceptor();

List<ClientHttpRequestInterceptor> restTemplateInterceptors = restTemplate.getInterceptors();
if (restTemplateInterceptors.stream()
.noneMatch(
interceptor -> interceptor.getClass() == instrumentationInterceptor.getClass())) {
restTemplateInterceptors.add(0, instrumentationInterceptor);
}
return restTemplate;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.spring.autoconfigure.v4_0.internal.instrumentation.web;

import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.instrumentation.spring.autoconfigure.internal.ConditionalOnEnabledInstrumentation;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.restclient.RestTemplateCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

/**
* Configures {@link RestTemplate} for tracing.
*
* <p>Adds OpenTelemetry instrumentation to RestTemplate beans after initialization.
*
* <p>This class is internal and is hence not for public use. Its APIs are unstable and can change
* at any time.
*/
@ConditionalOnEnabledInstrumentation(module = "spring-web")
@ConditionalOnClass(RestTemplate.class)
@Configuration
public class SpringWebInstrumentationAutoConfiguration {

public SpringWebInstrumentationAutoConfiguration() {}

// static to avoid "is not eligible for getting processed by all BeanPostProcessors" warning
@Bean
static RestTemplateBeanPostProcessor otelRestTemplateBeanPostProcessor(
ObjectProvider<OpenTelemetry> openTelemetryProvider,
ObjectProvider<ConfigProperties> configPropertiesProvider) {
return new RestTemplateBeanPostProcessor(openTelemetryProvider, configPropertiesProvider);
}

@Bean
RestTemplateCustomizer otelRestTemplateCustomizer(
ObjectProvider<OpenTelemetry> openTelemetryProvider,
ObjectProvider<ConfigProperties> configPropertiesProvider) {
return restTemplate ->
RestTemplateInstrumentation.addIfNotPresent(
restTemplate, openTelemetryProvider.getObject(), configPropertiesProvider.getObject());
}
}
Loading
Loading