diff --git a/.ci/load/Jenkinsfile b/.ci/load/Jenkinsfile index 1f40d81292..8aa328a5fb 100644 --- a/.ci/load/Jenkinsfile +++ b/.ci/load/Jenkinsfile @@ -81,8 +81,8 @@ pipeline { echo "Switch to requested agent version = ${params.apm_version}" sh(script: "git checkout ${params.apm_version}") - echo 'Building agent with Java 11' - sh(script: "JAVA_HOME=${env.HUDSON_HOME}/.java/java11 ./mvnw --batch-mode clean package -DskipTests=true -Dmaven.javadoc.skip=true -Dmaven.sources.skip=true") + echo 'Building agent with Java 17' + sh(script: "JAVA_HOME=${env.HUDSON_HOME}/.java/jdk17 ./mvnw --batch-mode clean package -DskipTests=true -Dmaven.javadoc.skip=true -Dmaven.sources.skip=true") // copy agent jar to a known filename that don't change with version sh(script: "cp -v \$(find ./elastic-apm-agent/target -name '*.jar' | grep -v sources | grep -v original | grep -v javadoc) ${WORKSPACE}/elastic-apm-agent.jar") diff --git a/.ci/packer_cache.sh b/.ci/packer_cache.sh index b37b6f9b0b..5fe042b881 100755 --- a/.ci/packer_cache.sh +++ b/.ci/packer_cache.sh @@ -3,7 +3,7 @@ set +e source /usr/local/bin/bash_standard_lib.sh -retry 3 JAVA_HOME=$HOME/.java/java11 \ +retry 3 JAVA_HOME=$HOME/.java/jdk17 \ ./mvnw clean package \ -q -B \ -DskipTests=true \ diff --git a/.ci/release/Jenkinsfile b/.ci/release/Jenkinsfile index deb3259002..c2883860a4 100644 --- a/.ci/release/Jenkinsfile +++ b/.ci/release/Jenkinsfile @@ -11,7 +11,7 @@ pipeline { NEXUS_SECRET = 'secret/apm-team/ci/nexus' MAVEN_CONFIG = '-B -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn -Dmaven.repo.local=.m2' HOME = "${env.WORKSPACE}" - JAVA_HOME = "${env.HUDSON_HOME}/.java/java10" + JAVA_HOME = "${env.HUDSON_HOME}/.java/jdk17" PATH = "${env.JAVA_HOME}/bin:${env.PATH}" SLACK_CHANNEL = '#apm-agent-java' NOTIFY_TO = 'build-apm+apm-agent-java@elastic.co' diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index e500b6d54b..cfdf0c3313 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -35,6 +35,7 @@ endif::[] * Add warning when agent is accidentally started on a JVM/JDK command-line tool - {pull}2924[#2924] * Fix `NullPointerException` caused by the Elasticsearch REST client instrumentation when collecting dropped span metrics - {pull}2959[#2959] * Fix SQS Instrumentation for Non-MessageReceive actions to avoid NoSuchElementException - {pull}2979[#2979] +* Fix `java.lang.NoSuchMethodError` when using the agent with WebFlux and Spring 6.x/Spring Boot 3.x - {pull}2935[#2935] ===== Potentially breaking changes diff --git a/Jenkinsfile b/Jenkinsfile index c6093e3a67..93dd59b780 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -31,7 +31,7 @@ pipeline { issueCommentTrigger("(${obltGitHubComments()}|^run (jdk compatibility|benchmark|integration|windows) tests)") } parameters { - string(name: 'JAVA_VERSION', defaultValue: 'java11', description: 'Java version to build & test') + string(name: 'JAVA_VERSION', defaultValue: 'jdk17', description: 'Java version to build & test') string(name: 'MAVEN_CONFIG', defaultValue: '-V -B -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn -Dhttps.protocols=TLSv1.2 -Dmaven.wagon.http.retryHandler.count=3 -Dmaven.wagon.httpconnectionManager.ttlSeconds=25', description: 'Additional maven options.') // Note about GH checks and optional steps @@ -362,21 +362,21 @@ pipeline { } } } - environment { - PATH = "${env.JAVA_HOME}/bin:${env.PATH}" - } matrix { agent { label 'linux && immutable' } axes { axis { - // the list of support java versions can be found in the infra repo (ansible/roles/java/defaults/main.yml) + // the list of supported java versions can be found in the infra repo (ansible/roles/java/defaults/main.yml) name 'JDK_VERSION' - // 'openjdk18' disabled for now see https://github.com/elastic/apm-agent-java/issues/2328 - values 'openjdk17' + values 'java11', 'jdk19' } } stages { stage('JDK Unit Tests') { + environment { + JAVA_HOME = "${env.HUDSON_HOME}/.java/${env.JDK_VERSION}" + PATH = "${env.JAVA_HOME}/bin:${env.PATH}" + } steps { withGithubNotify(context: "${STAGE_NAME} ${JDK_VERSION}", tab: 'tests') { deleteDir() diff --git a/apm-agent-attach-cli/src/main/java/co/elastic/apm/attach/AgentDownloader.java b/apm-agent-attach-cli/src/main/java/co/elastic/apm/attach/AgentDownloader.java index bf6c435f53..a1e7c7e66e 100644 --- a/apm-agent-attach-cli/src/main/java/co/elastic/apm/attach/AgentDownloader.java +++ b/apm-agent-attach-cli/src/main/java/co/elastic/apm/attach/AgentDownloader.java @@ -18,7 +18,7 @@ */ package co.elastic.apm.attach; -import co.elastic.apm.agent.util.Version; +import co.elastic.apm.agent.common.util.Version; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; diff --git a/apm-agent-attach-cli/src/test/java/co/elastic/apm/attach/AgentDownloaderTest.java b/apm-agent-attach-cli/src/test/java/co/elastic/apm/attach/AgentDownloaderTest.java index ac9a08fa21..6b4b2c0271 100644 --- a/apm-agent-attach-cli/src/test/java/co/elastic/apm/attach/AgentDownloaderTest.java +++ b/apm-agent-attach-cli/src/test/java/co/elastic/apm/attach/AgentDownloaderTest.java @@ -18,7 +18,7 @@ */ package co.elastic.apm.attach; -import co.elastic.apm.agent.util.Version; +import co.elastic.apm.agent.common.util.Version; import co.elastic.apm.attach.bouncycastle.BouncyCastleVerifier; import org.junit.jupiter.api.Test; diff --git a/apm-agent-common/src/main/java/co/elastic/apm/agent/util/Version.java b/apm-agent-common/src/main/java/co/elastic/apm/agent/common/util/Version.java similarity index 98% rename from apm-agent-common/src/main/java/co/elastic/apm/agent/util/Version.java rename to apm-agent-common/src/main/java/co/elastic/apm/agent/common/util/Version.java index 49436f1f9e..ac92153e2d 100644 --- a/apm-agent-common/src/main/java/co/elastic/apm/agent/util/Version.java +++ b/apm-agent-common/src/main/java/co/elastic/apm/agent/common/util/Version.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package co.elastic.apm.agent.util; +package co.elastic.apm.agent.common.util; import java.util.Arrays; import java.util.Objects; diff --git a/apm-agent-common/src/test/java/co/elastic/apm/agent/util/VersionTest.java b/apm-agent-common/src/test/java/co/elastic/apm/agent/util/VersionTest.java index 5c5e79b620..baf7011db9 100644 --- a/apm-agent-common/src/test/java/co/elastic/apm/agent/util/VersionTest.java +++ b/apm-agent-common/src/test/java/co/elastic/apm/agent/util/VersionTest.java @@ -18,6 +18,7 @@ */ package co.elastic.apm.agent.util; +import co.elastic.apm.agent.common.util.Version; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/CustomElementMatchers.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/CustomElementMatchers.java index 27b023cf17..113322d130 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/CustomElementMatchers.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/CustomElementMatchers.java @@ -23,7 +23,7 @@ import co.elastic.apm.agent.sdk.weakconcurrent.WeakConcurrent; import co.elastic.apm.agent.sdk.weakconcurrent.WeakMap; import co.elastic.apm.agent.util.ClassLoaderUtils; -import co.elastic.apm.agent.util.Version; +import co.elastic.apm.agent.common.util.Version; import net.bytebuddy.description.NamedElement; import net.bytebuddy.description.annotation.AnnotationSource; import net.bytebuddy.description.method.MethodDescription; diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/report/ApmServerClient.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/report/ApmServerClient.java index 1ee37d0757..e40b247ed8 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/report/ApmServerClient.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/report/ApmServerClient.java @@ -21,7 +21,7 @@ import co.elastic.apm.agent.configuration.CoreConfiguration; import co.elastic.apm.agent.report.ssl.SslUtils; import co.elastic.apm.agent.util.UrlConnectionUtils; -import co.elastic.apm.agent.util.Version; +import co.elastic.apm.agent.common.util.Version; import co.elastic.apm.agent.util.VersionUtils; import co.elastic.apm.agent.sdk.logging.Logger; import co.elastic.apm.agent.sdk.logging.LoggerFactory; diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/report/ApmServerHealthChecker.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/report/ApmServerHealthChecker.java index f0f3213800..275ebd07d1 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/report/ApmServerHealthChecker.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/report/ApmServerHealthChecker.java @@ -19,7 +19,7 @@ package co.elastic.apm.agent.report; import co.elastic.apm.agent.util.ExecutorUtils; -import co.elastic.apm.agent.util.Version; +import co.elastic.apm.agent.common.util.Version; import com.dslplatform.json.DslJson; import com.dslplatform.json.JsonReader; import com.dslplatform.json.Nullable; diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/springwebflux/SpringWebVersionUtils.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/springwebflux/SpringWebVersionUtils.java new file mode 100644 index 0000000000..2451029042 --- /dev/null +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/springwebflux/SpringWebVersionUtils.java @@ -0,0 +1,98 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. 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 co.elastic.apm.agent.springwebflux; + +import javax.annotation.Nullable; + +/** + * This class must be located within the same package as used by the WebFlux instrumentation, otherwise it will be loaded by the + * agent class loader, rather than the WebFlux plugin class loader. If the {@link ISpringWebVersionUtils} implementations are loaded + * anywhere other than the WebFlux plugin class loader, their loading will cause a {@link LinkageError}. + */ +public class SpringWebVersionUtils { + + private static final String SPRING_WEB_5_UTILS_CLASS_NAME = "co.elastic.apm.agent.springwebflux.SpringWeb5Utils"; + private static final String SPRING_WEB_6_UTILS_CLASS_NAME = "co.elastic.apm.agent.springwebflux.SpringWeb6Utils"; + + @Nullable + private static ISpringWebVersionUtils instance = null; + + private static volatile boolean initialized = false; + + private static synchronized void initialize() throws Exception { + if (initialized) { + return; + } + try { + // check if using spring 6.0.0 or higher + Class.forName("org.springframework.http.HttpStatusCode"); + try { + // loading class by name so to avoid linkage attempt when spring-web 6 is unavailable + instance = (ISpringWebVersionUtils) Class.forName(SPRING_WEB_6_UTILS_CLASS_NAME).getDeclaredConstructor().newInstance(); + } catch (Exception e) { + throw new IllegalStateException("Spring-web 6.x+ identified, but failed to load related utility class", e); + } + } catch (ClassNotFoundException ignored) { + // assuming spring-web < 6.x + try { + // loading class by name so to avoid linkage attempt on spring-web 6, where the getStatusCode API has changed + instance = (ISpringWebVersionUtils) Class.forName(SPRING_WEB_5_UTILS_CLASS_NAME).getDeclaredConstructor().newInstance(); + } catch (Exception e) { + throw new IllegalStateException("Spring-web < 6.x identified, but failed to load related utility class", e); + } + } finally { + initialized = true; + } + } + + @Nullable + private static ISpringWebVersionUtils getImplementation() throws Exception { + if (!initialized) { + initialize(); + } + return instance; + } + + /** + * A utility method to get the status code of a {@code org.springframework.http.server.reactive.ServerHttpResponse} from any version + * of Spring framework. + * + * @param response must be of type {@code org.springframework.http.server.reactive.ServerHttpResponse}, otherwise an Exception is + * expected + * @return the status code of the provided response + */ + public static int getStatusCode(Object response) throws Exception { + ISpringWebVersionUtils implementation = getImplementation(); + if (implementation != null) { + return implementation.getStatusCode(response); + } + return 200; + } + + public interface ISpringWebVersionUtils { + /** + * A utility method to get the status code of a {@code org.springframework.http.server.reactive.ServerHttpResponse} from any version + * of Spring framework. + * + * @param response must be of type {@code org.springframework.http.server.reactive.ServerHttpResponse} + * @return the corresponding status code + */ + int getStatusCode(Object response); + } +} diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/springwebflux/package-info.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/springwebflux/package-info.java new file mode 100644 index 0000000000..86685638b7 --- /dev/null +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/springwebflux/package-info.java @@ -0,0 +1,26 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/** + * This package is common as it needs to be used by Spring WebFlux instrumentations from different modules (compiled by different Java + * versions). It must have the same package name as used by the WebFlux instrumentation, as it is expected to be loaded by the same + * plugin class loader that loads the WebFlux advice and helper classes. + */ +package co.elastic.apm.agent.springwebflux; + diff --git a/apm-agent-core/src/test/java/co/elastic/apm/agent/MockTracer.java b/apm-agent-core/src/test/java/co/elastic/apm/agent/MockTracer.java index 5af6fc9d5e..f8f68d9b75 100644 --- a/apm-agent-core/src/test/java/co/elastic/apm/agent/MockTracer.java +++ b/apm-agent-core/src/test/java/co/elastic/apm/agent/MockTracer.java @@ -25,7 +25,7 @@ import co.elastic.apm.agent.objectpool.TestObjectPoolFactory; import co.elastic.apm.agent.report.ApmServerClient; import co.elastic.apm.agent.report.Reporter; -import co.elastic.apm.agent.util.Version; +import co.elastic.apm.agent.common.util.Version; import org.stagemonitor.configuration.ConfigurationRegistry; import static org.mockito.ArgumentMatchers.any; diff --git a/apm-agent-core/src/test/java/co/elastic/apm/agent/report/ApmServerClientTest.java b/apm-agent-core/src/test/java/co/elastic/apm/agent/report/ApmServerClientTest.java index abee0dc648..c268a10c5f 100644 --- a/apm-agent-core/src/test/java/co/elastic/apm/agent/report/ApmServerClientTest.java +++ b/apm-agent-core/src/test/java/co/elastic/apm/agent/report/ApmServerClientTest.java @@ -29,7 +29,7 @@ import co.elastic.apm.agent.impl.transaction.Transaction; import co.elastic.apm.agent.objectpool.TestObjectPoolFactory; import co.elastic.apm.agent.objectpool.impl.BookkeeperObjectPool; -import co.elastic.apm.agent.util.Version; +import co.elastic.apm.agent.common.util.Version; import com.github.tomakehurst.wiremock.core.WireMockConfiguration; import com.github.tomakehurst.wiremock.junit.WireMockRule; import org.assertj.core.util.Lists; diff --git a/apm-agent-core/src/test/java/co/elastic/apm/agent/report/ApmServerHealthCheckerTest.java b/apm-agent-core/src/test/java/co/elastic/apm/agent/report/ApmServerHealthCheckerTest.java index 9056e66241..51e5ef978e 100644 --- a/apm-agent-core/src/test/java/co/elastic/apm/agent/report/ApmServerHealthCheckerTest.java +++ b/apm-agent-core/src/test/java/co/elastic/apm/agent/report/ApmServerHealthCheckerTest.java @@ -18,7 +18,7 @@ */ package co.elastic.apm.agent.report; -import co.elastic.apm.agent.util.Version; +import co.elastic.apm.agent.common.util.Version; import org.junit.jupiter.api.Test; import java.io.IOException; diff --git a/apm-agent-core/src/test/java/co/elastic/apm/agent/report/IntakeV2ReportingEventHandlerTest.java b/apm-agent-core/src/test/java/co/elastic/apm/agent/report/IntakeV2ReportingEventHandlerTest.java index dcb2285dd1..9da16bce93 100644 --- a/apm-agent-core/src/test/java/co/elastic/apm/agent/report/IntakeV2ReportingEventHandlerTest.java +++ b/apm-agent-core/src/test/java/co/elastic/apm/agent/report/IntakeV2ReportingEventHandlerTest.java @@ -31,7 +31,7 @@ import co.elastic.apm.agent.impl.transaction.Transaction; import co.elastic.apm.agent.report.processor.ProcessorEventHandler; import co.elastic.apm.agent.report.serialize.DslJsonSerializer; -import co.elastic.apm.agent.util.Version; +import co.elastic.apm.agent.common.util.Version; import com.dslplatform.json.DslJson; import com.dslplatform.json.JsonWriter; import com.fasterxml.jackson.databind.JsonNode; diff --git a/apm-agent-core/src/test/java/co/elastic/apm/agent/util/PrivilegedActionUtilsTest.java b/apm-agent-core/src/test/java/co/elastic/apm/agent/util/PrivilegedActionUtilsTest.java index ba7ca695bc..a4dfd53dda 100644 --- a/apm-agent-core/src/test/java/co/elastic/apm/agent/util/PrivilegedActionUtilsTest.java +++ b/apm-agent-core/src/test/java/co/elastic/apm/agent/util/PrivilegedActionUtilsTest.java @@ -19,12 +19,13 @@ package co.elastic.apm.agent.util; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledForJreRange; +import org.junit.jupiter.api.condition.JRE; import org.junit.jupiter.api.io.TempDir; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; -import java.nio.file.FileAlreadyExistsException; import java.nio.file.Files; import java.nio.file.Path; import java.security.AccessController; @@ -37,6 +38,7 @@ import static co.elastic.apm.agent.testutils.assertions.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +@EnabledForJreRange(max = JRE.JAVA_17, disabledReason = "SecurityManager is not supported anymore") class PrivilegedActionUtilsTest { private static final AtomicBoolean enabled = new AtomicBoolean(false); diff --git a/apm-agent-java-17/apm-agent-spring-6/pom.xml b/apm-agent-java-17/apm-agent-spring-6/pom.xml new file mode 100755 index 0000000000..14f40051e2 --- /dev/null +++ b/apm-agent-java-17/apm-agent-spring-6/pom.xml @@ -0,0 +1,38 @@ + + + 4.0.0 + + + co.elastic.apm + apm-agent-java-17 + 1.35.1-SNAPSHOT + + + apm-agent-spring-6 + ${project.groupId}:${project.artifactId} + + + ${project.basedir}/../.. + + + + + ${project.groupId} + apm-agent-core + ${project.version} + + + org.springframework + spring-web + 6.0.1 + provided + + + + io.projectreactor + reactor-core + 3.2.0.RELEASE + test + + + diff --git a/apm-agent-java-17/apm-agent-spring-6/src/main/java/co/elastic/apm/agent/springwebflux/SpringWeb6Utils.java b/apm-agent-java-17/apm-agent-spring-6/src/main/java/co/elastic/apm/agent/springwebflux/SpringWeb6Utils.java new file mode 100644 index 0000000000..86ed8487d6 --- /dev/null +++ b/apm-agent-java-17/apm-agent-spring-6/src/main/java/co/elastic/apm/agent/springwebflux/SpringWeb6Utils.java @@ -0,0 +1,34 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. 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 co.elastic.apm.agent.springwebflux; + +import org.springframework.http.HttpStatusCode; +import org.springframework.http.server.reactive.ServerHttpResponse; + +/** + * This class is compiled with spring-web 6.x, as it relies on {@link HttpStatusCode} and an API that was introduced in 6.0.0. + * Therefore, it MUST only be loaded through its class name through {@link SpringWebVersionUtils}. + */ +public class SpringWeb6Utils implements SpringWebVersionUtils.ISpringWebVersionUtils { + @Override + public int getStatusCode(Object response) { + HttpStatusCode statusCode = ((ServerHttpResponse) response).getStatusCode(); + return statusCode != null ? statusCode.value() : 200; + } +} diff --git a/apm-agent-java-17/apm-agent-spring-6/src/test/java/co/elastic/apm/agent/springwebflux/SpringWeb6UtilsTest.java b/apm-agent-java-17/apm-agent-spring-6/src/test/java/co/elastic/apm/agent/springwebflux/SpringWeb6UtilsTest.java new file mode 100644 index 0000000000..ab6bff651f --- /dev/null +++ b/apm-agent-java-17/apm-agent-spring-6/src/test/java/co/elastic/apm/agent/springwebflux/SpringWeb6UtilsTest.java @@ -0,0 +1,43 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. 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 co.elastic.apm.agent.springwebflux; + +import org.junit.jupiter.api.Test; +import org.springframework.http.HttpStatusCode; +import org.springframework.http.server.reactive.ServerHttpResponse; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; + +public class SpringWeb6UtilsTest { + + @Test + void testGetStatusCode() throws Exception { + ServerHttpResponse mockResponse = mock(ServerHttpResponse.class); + doReturn(HttpStatusCode.valueOf(222)).when(mockResponse).getStatusCode(); + assertThat(SpringWebVersionUtils.getStatusCode(mockResponse)).isEqualTo(222); + } + + @Test + void testWrongResponseType() { + assertThatThrownBy(() -> SpringWebVersionUtils.getStatusCode(new Object())).isInstanceOf(ClassCastException.class); + } +} diff --git a/apm-agent-java-17/pom.xml b/apm-agent-java-17/pom.xml new file mode 100644 index 0000000000..ceaac15c5f --- /dev/null +++ b/apm-agent-java-17/pom.xml @@ -0,0 +1,27 @@ + + + 4.0.0 + + + apm-agent-parent + co.elastic.apm + 1.35.1-SNAPSHOT + + + apm-agent-java-17 + pom + ${project.groupId}:${project.artifactId} + + + apm-agent-spring-6 + + + + + ${project.basedir}/.. + + + true + + + diff --git a/apm-agent-plugins/apm-dubbo-plugin/pom.xml b/apm-agent-plugins/apm-dubbo-plugin/pom.xml index 8600daf4aa..7baa24ffa3 100644 --- a/apm-agent-plugins/apm-dubbo-plugin/pom.xml +++ b/apm-agent-plugins/apm-dubbo-plugin/pom.xml @@ -55,4 +55,17 @@ test + + + + + maven-surefire-plugin + + + --add-opens java.base/java.lang=ALL-UNNAMED + + + + + diff --git a/apm-agent-plugins/apm-javalin-plugin/pom.xml b/apm-agent-plugins/apm-javalin-plugin/pom.xml index 6c566756fb..1226310543 100644 --- a/apm-agent-plugins/apm-javalin-plugin/pom.xml +++ b/apm-agent-plugins/apm-javalin-plugin/pom.xml @@ -57,4 +57,16 @@ + + + + maven-surefire-plugin + + + --enable-preview + + + + + diff --git a/apm-agent-plugins/apm-mongodb/apm-mongodb4-plugin/src/test/java/co/elastic/apm/agent/mongodb/v4/Mongo4VersionIT.java b/apm-agent-plugins/apm-mongodb/apm-mongodb4-plugin/src/test/java/co/elastic/apm/agent/mongodb/v4/Mongo4VersionIT.java index 88a4c60e88..0dca50f024 100644 --- a/apm-agent-plugins/apm-mongodb/apm-mongodb4-plugin/src/test/java/co/elastic/apm/agent/mongodb/v4/Mongo4VersionIT.java +++ b/apm-agent-plugins/apm-mongodb/apm-mongodb4-plugin/src/test/java/co/elastic/apm/agent/mongodb/v4/Mongo4VersionIT.java @@ -20,7 +20,7 @@ import co.elastic.apm.agent.testutils.JUnit4TestClassWithDependencyRunner; import co.elastic.apm.agent.mongodb.AbstractMongoClientInstrumentationIT; -import co.elastic.apm.agent.util.Version; +import co.elastic.apm.agent.common.util.Version; import org.junit.After; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/apm-agent-plugins/apm-okhttp-plugin/src/test/java/co/elastic/apm/agent/okhttp/OkHttp3ClientAsyncInstrumentationTest.java b/apm-agent-plugins/apm-okhttp-plugin/src/test/java/co/elastic/apm/agent/okhttp/OkHttp3ClientAsyncInstrumentationTest.java index 9ad9677662..45a3463e70 100644 --- a/apm-agent-plugins/apm-okhttp-plugin/src/test/java/co/elastic/apm/agent/okhttp/OkHttp3ClientAsyncInstrumentationTest.java +++ b/apm-agent-plugins/apm-okhttp-plugin/src/test/java/co/elastic/apm/agent/okhttp/OkHttp3ClientAsyncInstrumentationTest.java @@ -19,7 +19,7 @@ package co.elastic.apm.agent.okhttp; import co.elastic.apm.agent.httpclient.AbstractHttpClientInstrumentationTest; -import co.elastic.apm.agent.util.Version; +import co.elastic.apm.agent.common.util.Version; import co.elastic.apm.agent.util.VersionUtils; import okhttp3.Call; import okhttp3.Callback; diff --git a/apm-agent-plugins/apm-okhttp-plugin/src/test/java/co/elastic/apm/agent/okhttp/OkHttp3ClientInstrumentationTest.java b/apm-agent-plugins/apm-okhttp-plugin/src/test/java/co/elastic/apm/agent/okhttp/OkHttp3ClientInstrumentationTest.java index ab4281c688..3608f1f142 100644 --- a/apm-agent-plugins/apm-okhttp-plugin/src/test/java/co/elastic/apm/agent/okhttp/OkHttp3ClientInstrumentationTest.java +++ b/apm-agent-plugins/apm-okhttp-plugin/src/test/java/co/elastic/apm/agent/okhttp/OkHttp3ClientInstrumentationTest.java @@ -19,7 +19,7 @@ package co.elastic.apm.agent.okhttp; import co.elastic.apm.agent.httpclient.AbstractHttpClientInstrumentationTest; -import co.elastic.apm.agent.util.Version; +import co.elastic.apm.agent.common.util.Version; import co.elastic.apm.agent.util.VersionUtils; import okhttp3.OkHttpClient; import okhttp3.Request; diff --git a/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-plugin/pom.xml b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-plugin/pom.xml index 8091850618..dbe54a411b 100644 --- a/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-plugin/pom.xml +++ b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-plugin/pom.xml @@ -92,7 +92,6 @@ test - diff --git a/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-plugin/src/main/java/co/elastic/apm/agent/springwebflux/SpringWeb5Utils.java b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-plugin/src/main/java/co/elastic/apm/agent/springwebflux/SpringWeb5Utils.java new file mode 100644 index 0000000000..4acda6f375 --- /dev/null +++ b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-plugin/src/main/java/co/elastic/apm/agent/springwebflux/SpringWeb5Utils.java @@ -0,0 +1,35 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. 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 co.elastic.apm.agent.springwebflux; + +import org.springframework.http.HttpStatus; +import org.springframework.http.server.reactive.ServerHttpResponse; + +/** + * This class is compiled with spring-web 5.x, relying on the {@link ServerHttpResponse#getStatusCode()}, which changed in 6.0.0. + * Therefore, it MUST only be loaded through its class name through {@link SpringWebVersionUtils}. + */ +public class SpringWeb5Utils implements SpringWebVersionUtils.ISpringWebVersionUtils { + + @Override + public int getStatusCode(Object response) { + HttpStatus statusCode = ((ServerHttpResponse) response).getStatusCode(); + return statusCode != null ? statusCode.value() : 200; + } +} diff --git a/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-plugin/src/main/java/co/elastic/apm/agent/springwebflux/WebfluxHelper.java b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-plugin/src/main/java/co/elastic/apm/agent/springwebflux/WebfluxHelper.java index 1c0e31f72b..aaeba84ea1 100644 --- a/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-plugin/src/main/java/co/elastic/apm/agent/springwebflux/WebfluxHelper.java +++ b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-plugin/src/main/java/co/elastic/apm/agent/springwebflux/WebfluxHelper.java @@ -31,13 +31,13 @@ import co.elastic.apm.agent.sdk.logging.LoggerFactory; import co.elastic.apm.agent.sdk.weakconcurrent.WeakConcurrent; import co.elastic.apm.agent.sdk.weakconcurrent.WeakMap; +import co.elastic.apm.agent.util.LoggerUtils; import co.elastic.apm.agent.util.PotentiallyMultiValuedMap; import co.elastic.apm.agent.util.PrivilegedActionUtils; import co.elastic.apm.agent.util.TransactionNameUtils; import org.reactivestreams.Publisher; import org.springframework.http.HttpCookie; import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.util.MultiValueMap; @@ -64,6 +64,7 @@ public class WebfluxHelper { private static final Logger log = LoggerFactory.getLogger(WebfluxHelper.class); + private static final Logger oneTimeResponseCodeErrorLogger = LoggerUtils.logOnce(log); public static final String TRANSACTION_ATTRIBUTE = WebfluxHelper.class.getName() + ".transaction"; private static final String SUBSCRIBER_ATTRIBUTE = WebfluxHelper.class.getName() + ".wrapped_subscriber"; @@ -270,8 +271,12 @@ private static void fillRequest(Transaction transaction, ServerWebExchange excha private static void fillResponse(Transaction transaction, ServerWebExchange exchange) { ServerHttpResponse serverResponse = exchange.getResponse(); - HttpStatus statusCode = serverResponse.getStatusCode(); - int status = statusCode != null ? statusCode.value() : 200; + int status = 0; + try { + status = SpringWebVersionUtils.getStatusCode(serverResponse); + } catch (Exception e) { + oneTimeResponseCodeErrorLogger.error("Failed to get response code", e); + } transaction.withResultIfUnset(ResultUtil.getResultByHttpStatus(status)); diff --git a/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-plugin/src/test/java/co/elastic/apm/agent/springwebflux/SpringWeb5UtilsTest.java b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-plugin/src/test/java/co/elastic/apm/agent/springwebflux/SpringWeb5UtilsTest.java new file mode 100644 index 0000000000..f0fb189b42 --- /dev/null +++ b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-plugin/src/test/java/co/elastic/apm/agent/springwebflux/SpringWeb5UtilsTest.java @@ -0,0 +1,43 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. 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 co.elastic.apm.agent.springwebflux; + +import org.junit.jupiter.api.Test; +import org.springframework.http.HttpStatus; +import org.springframework.http.server.reactive.ServerHttpResponse; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; + +class SpringWeb5UtilsTest { + + @Test + void testGetStatusCode() throws Exception { + ServerHttpResponse mockResponse = mock(ServerHttpResponse.class); + doReturn(HttpStatus.IM_USED).when(mockResponse).getStatusCode(); + assertThat(SpringWebVersionUtils.getStatusCode(mockResponse)).isEqualTo(226); + } + + @Test + void testWrongResponseType() { + assertThatThrownBy(() -> SpringWebVersionUtils.getStatusCode(new Object())).isInstanceOf(ClassCastException.class); + } +} diff --git a/apm-agent/pom.xml b/apm-agent/pom.xml index 8e036b4b09..1fc768b25f 100644 --- a/apm-agent/pom.xml +++ b/apm-agent/pom.xml @@ -20,7 +20,6 @@ ${project.basedir}/.. - @@ -402,6 +401,22 @@ + + + non-java-11 + + !11 + + + + ${project.groupId} + apm-agent-spring-6 + ${project.version} + + + + + diff --git a/integration-tests/external-plugin-test/external-plugin/pom.xml b/integration-tests/external-plugin-test/external-plugin/pom.xml index 212c395074..688221c67b 100644 --- a/integration-tests/external-plugin-test/external-plugin/pom.xml +++ b/integration-tests/external-plugin-test/external-plugin/pom.xml @@ -87,7 +87,7 @@ --> maven-shade-plugin - 3.2.4 + 3.3.0 package diff --git a/pom.xml b/pom.xml index a2889ff1d6..c108ed2e3d 100644 --- a/pom.xml +++ b/pom.xml @@ -221,6 +221,17 @@ + + + non-java-11 + + !11 + + + apm-agent-java-17 + + no-errorprone @@ -520,7 +531,7 @@ org.jacoco jacoco-maven-plugin - 0.8.7 + 0.8.8 org.codehaus.mojo @@ -539,7 +550,7 @@ maven-shade-plugin - 3.2.4 + 3.3.0 maven-clean-plugin