diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index 96f8f44bcd..853f4651ce 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -37,10 +37,11 @@ endif::[] [float] ===== Performance improvements * Proxy classes are excluded from instrumentation in more cases - {pull}2474[#2474] +* Only time type/method matching if the debug logging is enabled as the results are only used when debug logging is enabled - {pull}2471[#2471] [float] -===== Performance improvements -* Only time type/method matching if the debug logging is enabled as the results are only used when debug logging is enabled - {pull}2471[#2471] +===== Bug fixes +* Fix cross-plugin dependencies triggering NoClassDefFound - {pull}2509[#2509] [[release-notes-1.x]] === Java Agent version 1.x diff --git a/apm-agent-plugins/apm-apache-httpclient-plugin/src/main/java/co/elastic/apm/agent/httpclient/ApacheHttpAsyncClientInstrumentation.java b/apm-agent-plugins/apm-apache-httpclient-plugin/src/main/java/co/elastic/apm/agent/httpclient/ApacheHttpAsyncClientInstrumentation.java index ff833a7824..50cac22599 100644 --- a/apm-agent-plugins/apm-apache-httpclient-plugin/src/main/java/co/elastic/apm/agent/httpclient/ApacheHttpAsyncClientInstrumentation.java +++ b/apm-agent-plugins/apm-apache-httpclient-plugin/src/main/java/co/elastic/apm/agent/httpclient/ApacheHttpAsyncClientInstrumentation.java @@ -18,7 +18,6 @@ */ package co.elastic.apm.agent.httpclient; -import co.elastic.apm.agent.http.client.HttpClientHelper; import co.elastic.apm.agent.httpclient.helper.ApacheHttpAsyncClientHelper; import co.elastic.apm.agent.impl.transaction.AbstractSpan; import co.elastic.apm.agent.impl.transaction.Span; diff --git a/apm-agent-plugins/apm-apache-httpclient-plugin/src/main/java/co/elastic/apm/agent/httpclient/ApacheHttpClientInstrumentation.java b/apm-agent-plugins/apm-apache-httpclient-plugin/src/main/java/co/elastic/apm/agent/httpclient/ApacheHttpClientInstrumentation.java index 27e6e9bfee..29e94cdf3b 100644 --- a/apm-agent-plugins/apm-apache-httpclient-plugin/src/main/java/co/elastic/apm/agent/httpclient/ApacheHttpClientInstrumentation.java +++ b/apm-agent-plugins/apm-apache-httpclient-plugin/src/main/java/co/elastic/apm/agent/httpclient/ApacheHttpClientInstrumentation.java @@ -18,7 +18,6 @@ */ package co.elastic.apm.agent.httpclient; -import co.elastic.apm.agent.http.client.HttpClientHelper; import co.elastic.apm.agent.httpclient.helper.RequestHeaderAccessor; import co.elastic.apm.agent.impl.transaction.AbstractSpan; import co.elastic.apm.agent.impl.transaction.Outcome; diff --git a/apm-agent-plugins/apm-apache-httpclient-plugin/src/main/java/co/elastic/apm/agent/httpclient/LegacyApacheHttpClientInstrumentation.java b/apm-agent-plugins/apm-apache-httpclient-plugin/src/main/java/co/elastic/apm/agent/httpclient/LegacyApacheHttpClientInstrumentation.java index cc471d953d..807a5aaf4b 100644 --- a/apm-agent-plugins/apm-apache-httpclient-plugin/src/main/java/co/elastic/apm/agent/httpclient/LegacyApacheHttpClientInstrumentation.java +++ b/apm-agent-plugins/apm-apache-httpclient-plugin/src/main/java/co/elastic/apm/agent/httpclient/LegacyApacheHttpClientInstrumentation.java @@ -18,7 +18,6 @@ */ package co.elastic.apm.agent.httpclient; -import co.elastic.apm.agent.http.client.HttpClientHelper; import co.elastic.apm.agent.httpclient.helper.RequestHeaderAccessor; import co.elastic.apm.agent.impl.transaction.AbstractSpan; import co.elastic.apm.agent.impl.transaction.Outcome; diff --git a/apm-agent-plugins/apm-apache-httpclient-plugin/src/main/java/co/elastic/apm/agent/httpclient/helper/HttpAsyncRequestProducerWrapper.java b/apm-agent-plugins/apm-apache-httpclient-plugin/src/main/java/co/elastic/apm/agent/httpclient/helper/HttpAsyncRequestProducerWrapper.java index 641d31ff0d..87f074f0c2 100644 --- a/apm-agent-plugins/apm-apache-httpclient-plugin/src/main/java/co/elastic/apm/agent/httpclient/helper/HttpAsyncRequestProducerWrapper.java +++ b/apm-agent-plugins/apm-apache-httpclient-plugin/src/main/java/co/elastic/apm/agent/httpclient/helper/HttpAsyncRequestProducerWrapper.java @@ -18,7 +18,7 @@ */ package co.elastic.apm.agent.httpclient.helper; -import co.elastic.apm.agent.http.client.HttpClientHelper; +import co.elastic.apm.agent.httpclient.HttpClientHelper; import co.elastic.apm.agent.impl.transaction.AbstractSpan; import co.elastic.apm.agent.impl.transaction.Span; import co.elastic.apm.agent.impl.transaction.TraceContext; diff --git a/apm-agent-plugins/apm-asynchttpclient-plugin/src/main/java/co/elastic/apm/agent/asynchttpclient/AbstractAsyncHttpClientInstrumentation.java b/apm-agent-plugins/apm-asynchttpclient-plugin/src/main/java/co/elastic/apm/agent/asynchttpclient/AbstractAsyncHttpClientInstrumentation.java index 465ba8e1e9..170267d3df 100644 --- a/apm-agent-plugins/apm-asynchttpclient-plugin/src/main/java/co/elastic/apm/agent/asynchttpclient/AbstractAsyncHttpClientInstrumentation.java +++ b/apm-agent-plugins/apm-asynchttpclient-plugin/src/main/java/co/elastic/apm/agent/asynchttpclient/AbstractAsyncHttpClientInstrumentation.java @@ -20,7 +20,7 @@ import co.elastic.apm.agent.bci.TracerAwareInstrumentation; import co.elastic.apm.agent.collections.WeakConcurrentProviderImpl; -import co.elastic.apm.agent.http.client.HttpClientHelper; +import co.elastic.apm.agent.httpclient.HttpClientHelper; import co.elastic.apm.agent.impl.ElasticApmTracer; import co.elastic.apm.agent.impl.transaction.AbstractSpan; import co.elastic.apm.agent.impl.transaction.Outcome; diff --git a/apm-agent-plugins/apm-cassandra/apm-cassandra-core-plugin/pom.xml b/apm-agent-plugins/apm-cassandra/apm-cassandra-core-plugin/pom.xml index dcf994f8e3..a9eb008bcb 100644 --- a/apm-agent-plugins/apm-cassandra/apm-cassandra-core-plugin/pom.xml +++ b/apm-agent-plugins/apm-cassandra/apm-cassandra-core-plugin/pom.xml @@ -14,12 +14,4 @@ ${project.basedir}/../../.. - - - ${project.groupId} - apm-jdbc-plugin - ${project.version} - - - diff --git a/apm-agent-plugins/apm-httpclient-core/src/main/java/co/elastic/apm/agent/http/client/HttpClientHelper.java b/apm-agent-plugins/apm-httpclient-core/src/main/java/co/elastic/apm/agent/httpclient/HttpClientHelper.java similarity index 98% rename from apm-agent-plugins/apm-httpclient-core/src/main/java/co/elastic/apm/agent/http/client/HttpClientHelper.java rename to apm-agent-plugins/apm-httpclient-core/src/main/java/co/elastic/apm/agent/httpclient/HttpClientHelper.java index f1b6d1f7be..6aa657d7c3 100644 --- a/apm-agent-plugins/apm-httpclient-core/src/main/java/co/elastic/apm/agent/http/client/HttpClientHelper.java +++ b/apm-agent-plugins/apm-httpclient-core/src/main/java/co/elastic/apm/agent/httpclient/HttpClientHelper.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package co.elastic.apm.agent.http.client; +package co.elastic.apm.agent.httpclient; import co.elastic.apm.agent.impl.context.Destination; import co.elastic.apm.agent.impl.transaction.AbstractSpan; diff --git a/apm-agent-plugins/apm-httpclient-core/src/test/java/co/elastic/apm/agent/http/client/HttpClientHelperTest.java b/apm-agent-plugins/apm-httpclient-core/src/test/java/co/elastic/apm/agent/httpclient/HttpClientHelperTest.java similarity index 99% rename from apm-agent-plugins/apm-httpclient-core/src/test/java/co/elastic/apm/agent/http/client/HttpClientHelperTest.java rename to apm-agent-plugins/apm-httpclient-core/src/test/java/co/elastic/apm/agent/httpclient/HttpClientHelperTest.java index d27573a960..3c93b764ee 100644 --- a/apm-agent-plugins/apm-httpclient-core/src/test/java/co/elastic/apm/agent/http/client/HttpClientHelperTest.java +++ b/apm-agent-plugins/apm-httpclient-core/src/test/java/co/elastic/apm/agent/httpclient/HttpClientHelperTest.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package co.elastic.apm.agent.http.client; +package co.elastic.apm.agent.httpclient; import co.elastic.apm.agent.AbstractInstrumentationTest; import co.elastic.apm.agent.impl.context.Destination; diff --git a/apm-agent-plugins/apm-jdk-httpclient-plugin/src/main/java/co/elastic/apm/agent/httpclient/HttpClientAdviceHelper.java b/apm-agent-plugins/apm-jdk-httpclient-plugin/src/main/java/co/elastic/apm/agent/httpclient/HttpClientAdviceHelper.java index 033ee9c49e..abdfa7f46b 100644 --- a/apm-agent-plugins/apm-jdk-httpclient-plugin/src/main/java/co/elastic/apm/agent/httpclient/HttpClientAdviceHelper.java +++ b/apm-agent-plugins/apm-jdk-httpclient-plugin/src/main/java/co/elastic/apm/agent/httpclient/HttpClientAdviceHelper.java @@ -18,7 +18,6 @@ */ package co.elastic.apm.agent.httpclient; -import co.elastic.apm.agent.http.client.HttpClientHelper; import co.elastic.apm.agent.impl.GlobalTracer; import co.elastic.apm.agent.impl.Tracer; import co.elastic.apm.agent.impl.transaction.AbstractSpan; diff --git a/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttp3ClientAsyncInstrumentation.java b/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttp3ClientAsyncInstrumentation.java index 1c4966c42f..712787697d 100644 --- a/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttp3ClientAsyncInstrumentation.java +++ b/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttp3ClientAsyncInstrumentation.java @@ -18,7 +18,7 @@ */ package co.elastic.apm.agent.okhttp; -import co.elastic.apm.agent.http.client.HttpClientHelper; +import co.elastic.apm.agent.httpclient.HttpClientHelper; import co.elastic.apm.agent.impl.transaction.AbstractSpan; import co.elastic.apm.agent.impl.transaction.Outcome; import co.elastic.apm.agent.impl.transaction.Span; diff --git a/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttp3ClientInstrumentation.java b/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttp3ClientInstrumentation.java index c255285462..05bd8a643f 100644 --- a/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttp3ClientInstrumentation.java +++ b/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttp3ClientInstrumentation.java @@ -18,7 +18,7 @@ */ package co.elastic.apm.agent.okhttp; -import co.elastic.apm.agent.http.client.HttpClientHelper; +import co.elastic.apm.agent.httpclient.HttpClientHelper; import co.elastic.apm.agent.impl.transaction.AbstractSpan; import co.elastic.apm.agent.impl.transaction.Outcome; import co.elastic.apm.agent.impl.transaction.Span; diff --git a/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttpClientAsyncInstrumentation.java b/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttpClientAsyncInstrumentation.java index edc210c2f8..fe24707eea 100644 --- a/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttpClientAsyncInstrumentation.java +++ b/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttpClientAsyncInstrumentation.java @@ -18,7 +18,7 @@ */ package co.elastic.apm.agent.okhttp; -import co.elastic.apm.agent.http.client.HttpClientHelper; +import co.elastic.apm.agent.httpclient.HttpClientHelper; import co.elastic.apm.agent.impl.transaction.AbstractSpan; import co.elastic.apm.agent.impl.transaction.Outcome; import co.elastic.apm.agent.impl.transaction.Span; diff --git a/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttpClientInstrumentation.java b/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttpClientInstrumentation.java index a12e15796a..916692350c 100644 --- a/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttpClientInstrumentation.java +++ b/apm-agent-plugins/apm-okhttp-plugin/src/main/java/co/elastic/apm/agent/okhttp/OkHttpClientInstrumentation.java @@ -18,7 +18,7 @@ */ package co.elastic.apm.agent.okhttp; -import co.elastic.apm.agent.http.client.HttpClientHelper; +import co.elastic.apm.agent.httpclient.HttpClientHelper; import co.elastic.apm.agent.impl.transaction.AbstractSpan; import co.elastic.apm.agent.impl.transaction.Outcome; import co.elastic.apm.agent.impl.transaction.Span; diff --git a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/adapter/JakartaServletApiAdapter.java b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/adapter/JakartaServletApiAdapter.java index c2e8369675..51450af92d 100644 --- a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/adapter/JakartaServletApiAdapter.java +++ b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/adapter/JakartaServletApiAdapter.java @@ -41,7 +41,7 @@ public class JakartaServletApiAdapter implements ServletApiAdapter { - public static final JakartaServletApiAdapter INSTANCE = new JakartaServletApiAdapter(); + private static final JakartaServletApiAdapter INSTANCE = new JakartaServletApiAdapter(); private JakartaServletApiAdapter() { } diff --git a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/servicename/InitServiceNameInstrumentation.java b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/servicename/InitServiceNameInstrumentation.java index cbefb1e257..e57eacdde2 100644 --- a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/servicename/InitServiceNameInstrumentation.java +++ b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/servicename/InitServiceNameInstrumentation.java @@ -80,14 +80,15 @@ public ElementMatcher getMethodMatcher() { public static class JavaxInitServiceNameInstrumentation extends InitServiceNameInstrumentation { - private static final JavaxServletApiAdapter adapter = JavaxServletApiAdapter.get(); - @Override public String rootClassNameThatClassloaderCanLoad() { return "javax.servlet.AsyncContext"; } public static class AdviceClass { + + private static final JavaxServletApiAdapter adapter = JavaxServletApiAdapter.get(); + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) public static void onEnter(@Advice.Argument(0) @Nullable Object arg) { javax.servlet.ServletContext servletContext; @@ -107,8 +108,6 @@ public static void onEnter(@Advice.Argument(0) @Nullable Object arg) { public static class JakartaInitServiceNameInstrumentation extends InitServiceNameInstrumentation { - private static final JakartaServletApiAdapter adapter = JakartaServletApiAdapter.get(); - @Override public String rootClassNameThatClassloaderCanLoad() { return "jakarta.servlet.AsyncContext"; @@ -116,6 +115,8 @@ public String rootClassNameThatClassloaderCanLoad() { public static class AdviceClass { + private static final JakartaServletApiAdapter adapter = JakartaServletApiAdapter.get(); + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) public static void onEnter(@Advice.Argument(0) @Nullable Object arg) { jakarta.servlet.ServletContext servletContext; diff --git a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/servicename/ServletContainerInitializerServiceNameInstrumentation.java b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/servicename/ServletContainerInitializerServiceNameInstrumentation.java index 0c75c8b323..29b5177c92 100644 --- a/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/servicename/ServletContainerInitializerServiceNameInstrumentation.java +++ b/apm-agent-plugins/apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/servicename/ServletContainerInitializerServiceNameInstrumentation.java @@ -74,8 +74,6 @@ public ElementMatcher getMethodMatcher() { public static class JavaxInitServiceNameInstrumentation extends ServletContainerInitializerServiceNameInstrumentation { - private static final JavaxServletApiAdapter adapter = JavaxServletApiAdapter.get(); - @Override public String rootClassNameThatClassloaderCanLoad() { return "javax.servlet.AsyncContext"; @@ -83,6 +81,8 @@ public String rootClassNameThatClassloaderCanLoad() { public static class AdviceClass { + private static final JavaxServletApiAdapter adapter = JavaxServletApiAdapter.get(); + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) public static void onEnter(@Advice.Argument(1) @Nullable Object servletContext) { if (servletContext instanceof javax.servlet.ServletContext) { @@ -94,8 +94,6 @@ public static void onEnter(@Advice.Argument(1) @Nullable Object servletContext) public static class JakartaInitServiceNameInstrumentation extends ServletContainerInitializerServiceNameInstrumentation { - private static final JakartaServletApiAdapter adapter = JakartaServletApiAdapter.get(); - @Override public String rootClassNameThatClassloaderCanLoad() { return "jakarta.servlet.AsyncContext"; @@ -103,6 +101,8 @@ public String rootClassNameThatClassloaderCanLoad() { public static class AdviceClass { + private static final JakartaServletApiAdapter adapter = JakartaServletApiAdapter.get(); + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) public static void onEnter(@Advice.Argument(1) @Nullable Object servletContext) { if (servletContext instanceof jakarta.servlet.ServletContext) { diff --git a/apm-agent-plugins/apm-spring-resttemplate/apm-spring-resttemplate-plugin/src/main/java/co/elastic/apm/agent/resttemplate/SpringRestTemplateAdvice.java b/apm-agent-plugins/apm-spring-resttemplate/apm-spring-resttemplate-plugin/src/main/java/co/elastic/apm/agent/resttemplate/SpringRestTemplateAdvice.java index 8b732ea584..15bdde6228 100644 --- a/apm-agent-plugins/apm-spring-resttemplate/apm-spring-resttemplate-plugin/src/main/java/co/elastic/apm/agent/resttemplate/SpringRestTemplateAdvice.java +++ b/apm-agent-plugins/apm-spring-resttemplate/apm-spring-resttemplate-plugin/src/main/java/co/elastic/apm/agent/resttemplate/SpringRestTemplateAdvice.java @@ -19,7 +19,7 @@ package co.elastic.apm.agent.resttemplate; import co.elastic.apm.agent.bci.TracerAwareInstrumentation; -import co.elastic.apm.agent.http.client.HttpClientHelper; +import co.elastic.apm.agent.httpclient.HttpClientHelper; import co.elastic.apm.agent.impl.transaction.AbstractSpan; import co.elastic.apm.agent.impl.transaction.Outcome; import co.elastic.apm.agent.impl.transaction.Span; diff --git a/apm-agent-plugins/apm-spring-webmvc-plugin/src/main/java/co/elastic/apm/agent/springwebmvc/SpringMvcPluginClassLoaderRootPackageCustomizer.java b/apm-agent-plugins/apm-spring-webmvc-plugin/src/main/java/co/elastic/apm/agent/springwebmvc/SpringMvcPluginClassLoaderRootPackageCustomizer.java new file mode 100644 index 0000000000..419f72034d --- /dev/null +++ b/apm-agent-plugins/apm-spring-webmvc-plugin/src/main/java/co/elastic/apm/agent/springwebmvc/SpringMvcPluginClassLoaderRootPackageCustomizer.java @@ -0,0 +1,31 @@ +/* + * 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.springwebmvc; + +import co.elastic.apm.agent.bci.PluginClassLoaderRootPackageCustomizer; + +import java.util.Arrays; +import java.util.Collection; + +public class SpringMvcPluginClassLoaderRootPackageCustomizer extends PluginClassLoaderRootPackageCustomizer { + @Override + public Collection pluginClassLoaderRootPackages() { + return Arrays.asList(getPluginPackage(), "co.elastic.apm.agent.servlet"); + } +} diff --git a/apm-agent-plugins/apm-spring-webmvc-plugin/src/main/java/co/elastic/apm/agent/springwebmvc/SpringServiceNameInstrumentation.java b/apm-agent-plugins/apm-spring-webmvc-plugin/src/main/java/co/elastic/apm/agent/springwebmvc/SpringServiceNameInstrumentation.java index f7cd9204d8..f2e01c2887 100644 --- a/apm-agent-plugins/apm-spring-webmvc-plugin/src/main/java/co/elastic/apm/agent/springwebmvc/SpringServiceNameInstrumentation.java +++ b/apm-agent-plugins/apm-spring-webmvc-plugin/src/main/java/co/elastic/apm/agent/springwebmvc/SpringServiceNameInstrumentation.java @@ -22,6 +22,7 @@ import co.elastic.apm.agent.configuration.ServiceInfo; import co.elastic.apm.agent.servlet.ServletServiceNameHelper; import co.elastic.apm.agent.servlet.adapter.JavaxServletApiAdapter; +import co.elastic.apm.agent.servlet.adapter.ServletApiAdapter; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.NamedElement; import net.bytebuddy.description.method.MethodDescription; @@ -67,6 +68,8 @@ public String getAdviceClassName() { public static class SpringServiceNameAdvice { + private static final JavaxServletApiAdapter adapter = JavaxServletApiAdapter.get(); + @Advice.OnMethodExit(suppress = Throwable.class, inline = false) public static void afterInitPropertySources(@Advice.This WebApplicationContext applicationContext) { // avoid having two service names for a standalone jar @@ -74,6 +77,7 @@ public static void afterInitPropertySources(@Advice.This WebApplicationContext a if (!ServiceInfo.autoDetected().isMultiServiceContainer()) { return; } + // This method will be called whenever the spring application context is refreshed which may be more than once // // For example, using Tomcat Servlet container, it's called twice with the first not having a ServletContext, @@ -83,14 +87,10 @@ public static void afterInitPropertySources(@Advice.This WebApplicationContext a ServiceInfo fromServletContext = ServiceInfo.empty(); ServletContext servletContext = applicationContext.getServletContext(); if (servletContext != null) { - try { - ClassLoader servletClassloader = servletContext.getClassLoader(); - if (servletClassloader != null) { - classLoader = servletClassloader; - fromServletContext = ServletServiceNameHelper.detectServiceInfo(JavaxServletApiAdapter.get(), servletContext, servletClassloader); - } - } catch (UnsupportedOperationException e) { - // silently ignored + ClassLoader servletClassloader = adapter.getClassLoader(servletContext); + if (servletClassloader != null) { + classLoader = servletClassloader; + fromServletContext = ServletServiceNameHelper.detectServiceInfo(adapter, servletContext, servletClassloader); } } diff --git a/apm-agent-plugins/apm-spring-webmvc-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.bci.PluginClassLoaderRootPackageCustomizer b/apm-agent-plugins/apm-spring-webmvc-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.bci.PluginClassLoaderRootPackageCustomizer new file mode 100644 index 0000000000..5647dc8e5a --- /dev/null +++ b/apm-agent-plugins/apm-spring-webmvc-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.bci.PluginClassLoaderRootPackageCustomizer @@ -0,0 +1 @@ +co.elastic.apm.agent.springwebmvc.SpringMvcPluginClassLoaderRootPackageCustomizer diff --git a/apm-agent-plugins/apm-urlconnection-plugin/src/main/java/co/elastic/apm/agent/urlconnection/HttpUrlConnectionInstrumentation.java b/apm-agent-plugins/apm-urlconnection-plugin/src/main/java/co/elastic/apm/agent/urlconnection/HttpUrlConnectionInstrumentation.java index 22016ac984..370b38b2fa 100644 --- a/apm-agent-plugins/apm-urlconnection-plugin/src/main/java/co/elastic/apm/agent/urlconnection/HttpUrlConnectionInstrumentation.java +++ b/apm-agent-plugins/apm-urlconnection-plugin/src/main/java/co/elastic/apm/agent/urlconnection/HttpUrlConnectionInstrumentation.java @@ -20,7 +20,7 @@ import co.elastic.apm.agent.bci.TracerAwareInstrumentation; import co.elastic.apm.agent.collections.WeakConcurrentProviderImpl; -import co.elastic.apm.agent.http.client.HttpClientHelper; +import co.elastic.apm.agent.httpclient.HttpClientHelper; import co.elastic.apm.agent.impl.transaction.AbstractSpan; import co.elastic.apm.agent.impl.transaction.Outcome; import co.elastic.apm.agent.impl.transaction.Span; diff --git a/apm-agent-plugins/apm-vertx/apm-vertx-common/src/main/java/co/elastic/apm/agent/vertx/AbstractVertxWebClientHelper.java b/apm-agent-plugins/apm-vertx/apm-vertx-common/src/main/java/co/elastic/apm/agent/vertx/AbstractVertxWebClientHelper.java index 5ce9bea19c..24ff54fd64 100644 --- a/apm-agent-plugins/apm-vertx/apm-vertx-common/src/main/java/co/elastic/apm/agent/vertx/AbstractVertxWebClientHelper.java +++ b/apm-agent-plugins/apm-vertx/apm-vertx-common/src/main/java/co/elastic/apm/agent/vertx/AbstractVertxWebClientHelper.java @@ -18,7 +18,7 @@ */ package co.elastic.apm.agent.vertx; -import co.elastic.apm.agent.http.client.HttpClientHelper; +import co.elastic.apm.agent.httpclient.HttpClientHelper; import co.elastic.apm.agent.impl.transaction.AbstractSpan; import co.elastic.apm.agent.impl.transaction.Outcome; import co.elastic.apm.agent.impl.transaction.Span; diff --git a/apm-agent/src/test/java/co/elastic/apm/agent/PackagingTest.java b/apm-agent/src/test/java/co/elastic/apm/agent/PackagingTest.java index ebe5d0971c..13cfe78b4e 100644 --- a/apm-agent/src/test/java/co/elastic/apm/agent/PackagingTest.java +++ b/apm-agent/src/test/java/co/elastic/apm/agent/PackagingTest.java @@ -18,83 +18,300 @@ */ package co.elastic.apm.agent; +import co.elastic.apm.agent.bci.PluginClassLoaderRootPackageCustomizer; import org.apache.maven.model.Dependency; import org.apache.maven.model.Model; import org.apache.maven.model.io.xpp3.MavenXpp3Reader; import org.codehaus.plexus.util.xml.pull.XmlPullParserException; import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import javax.annotation.Nullable; import java.io.IOException; +import java.io.Reader; +import java.net.URISyntaxException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; import java.util.Comparator; -import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; +import java.util.stream.Stream; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; public class PackagingTest { + private static final Logger log = LoggerFactory.getLogger(PackagingTest.class); + + private static final Path MODULE_ROOT = getModuleRoot(); + private static final int MAX_DEPTH = 99; + @Test - void checkPluginDependencies() throws Exception { - // search for all known META-INF/services files + void checkPluginDependencies() { + Set pluginArtifactIds = getAgentPluginModules() + .filter(AgentModule::isPlugin) + .map(m -> m.mavenArtifactId) + .collect(Collectors.toSet()); - Path classLocation = Paths.get(PackagingTest.class.getProtectionDomain().getCodeSource().getLocation().toURI()); + checkContainsPluginAsDependencies(MODULE_ROOT.resolve("pom.xml"), pluginArtifactIds); + } - Path moduleRoot = classLocation.getParent().getParent(); - assertThat(moduleRoot).isDirectory(); + @Test + void checkPluginInterdependencies() { - // compute the list of all plugin classes by scanning service loader files from filesystem - Path pluginsModulePath = moduleRoot.getParent().resolve("apm-agent-plugins"); - Set serviceLoaderFiles = Files.find(pluginsModulePath, 10, (path, attributes) -> { - Path fileFolder = path.getParent(); - return Files.isRegularFile(path) && fileFolder.endsWith(Path.of("src", "main", "resources", "META-INF", "services")); - }) - .collect(Collectors.toSet()); + Stream plugins = getAgentPluginModules().filter(AgentModule::isPlugin); + Map modules = getAgentPluginModules().collect(Collectors.toMap(m -> m.mavenArtifactId, m -> m)); + plugins.forEach(plugin -> { - Set pluginArtifacts = new HashSet<>(); - serviceLoaderFiles.forEach(path -> { - Path pluginRoot = path.getParent().getParent().getParent().getParent().getParent().getParent(); - String pluginArtifact = getArtifactId(pluginRoot.resolve("pom.xml")); - pluginArtifacts.add(pluginArtifact); - }); + Set dependencies = plugin.getInternalDependencies().stream() + .filter(d -> !d.equals("apm-agent-core")) // filter-out explicit dependencies to apm-agent-core + .filter(d -> !d.equals("apm-log-shader-plugin-common")) // TODO : remove this known case when logging refactor PR is merged. + .map(modules::get) + .collect(Collectors.toSet()); - assertThat(pluginArtifacts).isNotEmpty(); - checkContainsPluginAsDependencies(moduleRoot.resolve("pom.xml"), pluginArtifacts); + for (AgentModule dep : dependencies) { + log.info("checking dependency from plugin '{}' to module '{}'", plugin.mavenArtifactId, dep.mavenArtifactId); + + if (plugin.basePackage.equals(dep.basePackage)) { + log.info("plugin '{}' with base package '{}' depends on module '{}' that have same base package", plugin.mavenArtifactId, plugin.basePackage, dep.mavenArtifactId); + } else { + log.info("plugin '{}' with base package '{}' depends on module '{}' with base package '{}'", plugin.mavenArtifactId, plugin.basePackage, dep.mavenArtifactId, dep.basePackage); + + Collection depRootPackages = dep.getClassloaderRootPackages(); + if (depRootPackages.isEmpty()) { + // dependency is loaded in the bootstrap CL, thus it is always visible to the plugin + log.info("dependency '{}' is loaded in the bootstrap CL, thus it is always visible to the '{}' plugin", dep.mavenArtifactId, plugin.mavenArtifactId); + } else if (!dep.hasNonTestDependencies()) { + log.info("dependency '{}' does not have dependencies, thus it is safe to load from agent or plugin CL", dep.mavenArtifactId); + } else if (plugin.getClassloaderRootPackages().contains(dep.basePackage)) { + log.info("dependency '{}' is accessible to plugin '{}' through classloader root customization", dep.mavenArtifactId, plugin.mavenArtifactId); + } else { + fail("dependency '%s' base package '%s' is NOT accessible to plugin '%s', using a PluginClassLoaderRootPackageCustomizer in plugin is required", dep.mavenArtifactId, dep.basePackage, plugin.mavenArtifactId); + } + + } + } + + }); + } - private static String getArtifactId(Path pomPath) { - MavenXpp3Reader reader = new MavenXpp3Reader(); + private static Stream getAgentPluginModules() { + // search for all maven submodules within the plugins directory + + Path pluginsModulePath = MODULE_ROOT.getParent().resolve("apm-agent-plugins"); + + Stream pomStream; try { - Model model = reader.read(Files.newBufferedReader(pomPath)); - return model.getArtifactId(); - } catch (IOException | XmlPullParserException e) { + pomStream = Files.find(pluginsModulePath, MAX_DEPTH, + (path, attributes) -> path.getFileName().toString().equals("pom.xml")) + .collect(Collectors.toSet()) + .stream(); + } catch (IOException e) { throw new IllegalStateException(e); } + + return pomStream + // use an intermediate map entry to keep track of file -> pom association + .map(file -> Map.entry(file, parseMaven(file))) + // only include real code dependencies, pom modules can be ignored + .filter(e -> e.getValue().getPackaging() == null || e.getValue().getPackaging().equals("jar")) + .map(e -> new AgentModule(e.getKey().getParent())); } - private static void checkContainsPluginAsDependencies(Path pomPath, Set plugins) { - assertThat(pomPath).isRegularFile(); + /** + * Represents a Java agent module, either a plugin or an internal dependency + */ + private static class AgentModule { + private final Path rootFolder; + private final Model mavenModel; + private final String mavenGroupId; + private final String mavenArtifactId; - MavenXpp3Reader reader = new MavenXpp3Reader(); - try { - Model model = reader.read(Files.newBufferedReader(pomPath)); - Set dependenciesArtifacts = model.getDependencies().stream() - .filter(d -> sameProjectGroup(model, d) && sameProjectVersion(model, d)) + @Nullable + private final String basePackage; + + private AgentModule(Path rootFolder) { + this.rootFolder = rootFolder.toAbsolutePath(); + this.mavenModel = parseMaven(rootFolder.resolve("pom.xml")); + this.mavenArtifactId = mavenModel.getArtifactId(); + this.basePackage = getBasePackage(rootFolder); + + // verify module invariants & store effective groupID + String groupId = mavenModel.getGroupId(); + if (groupId == null) { + assertThat(mavenModel.getParent()).isNotNull(); + groupId = mavenModel.getParent().getGroupId(); + } + this.mavenGroupId = groupId; + assertThat(groupId) + .isEqualTo("co.elastic.apm"); + } + + public boolean isPlugin() { + try { + Path servicesFolder = getServicesFolder(); + return servicesFolder != null && Files.list(servicesFolder).count() > 0; + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + public boolean hasCode() { + return basePackage != null; + } + + public Collection getClassloaderRootPackages() { + Path servicesFolder = getServicesFolder(); + if (servicesFolder != null) { + Path customizerService = servicesFolder.resolve(PluginClassLoaderRootPackageCustomizer.class.getName()); + if (Files.exists(customizerService)) { + try { + List lines = Files.readAllLines(customizerService); + assertThat(lines).describedAs("exactly one root package customizer expected in file %s", customizerService).hasSize(1); + Class customizerType = Class.forName(lines.get(0), true, PackagingTest.class.getClassLoader()); + PluginClassLoaderRootPackageCustomizer customizer = (PluginClassLoaderRootPackageCustomizer) customizerType.getConstructor().newInstance(); + return customizer.pluginClassLoaderRootPackages(); + } catch (Exception e) { + throw new IllegalStateException(e); + } + } + } + return Collections.singleton(basePackage); + } + + @Nullable + private Path getServicesFolder() { + Path servicesFolder = rootFolder.resolve(Path.of("src", "main", "resources", "META-INF", "services")); + return Files.isDirectory(servicesFolder) ? servicesFolder : null; + } + + public Set getInternalDependencies() { + return mavenModel.getDependencies() + .stream() + .filter(d -> getGroupId(d).equals(mavenGroupId)) + .filter(d -> d.getScope() == null || !d.getScope().equals("test")) .map(Dependency::getArtifactId) .collect(Collectors.toSet()); + } - assertThat(dependenciesArtifacts).describedAs(createDescription(plugins)).containsAll(plugins); + public boolean hasNonTestDependencies() { + return mavenModel.getDependencies().stream().anyMatch(d -> !"test".equals(d.getScope())); + } + + private String getGroupId(Dependency d) { + String groupId = d.getGroupId(); + return groupId.equals("${project.groupId}") ? mavenGroupId : groupId; + } + + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + AgentModule that = (AgentModule) o; + return rootFolder.equals(that.rootFolder); + } + + @Override + public int hashCode() { + return Objects.hash(rootFolder); + } + + @Override + public String toString() { + return "AgentPlugin{" + + "rootFolder=" + rootFolder + + ", mavenArtifactId='" + mavenArtifactId + '\'' + + ", basePackage='" + basePackage + '\'' + + '}'; + } + } + + private static Model parseMaven(Path pomPath) { + MavenXpp3Reader reader = new MavenXpp3Reader(); + try (Reader r = Files.newBufferedReader(pomPath)) { + return reader.read(r); } catch (IOException | XmlPullParserException e) { throw new IllegalStateException(e); } } + @Nullable + private static String getBasePackage(Path pluginRoot) { + Path javaSources = pluginRoot.resolve("src").resolve("main").resolve("java"); + + if (!Files.isDirectory(javaSources)) { + // no java sources, it s a test module + return null; + } + + Path expectedPrefix = javaSources.resolve("co").resolve("elastic").resolve("apm").resolve("agent"); + AtomicReference pluginSubPackage = new AtomicReference<>(); + + try { + Files.find(javaSources, MAX_DEPTH, (path, attributes) -> + Files.isRegularFile(path) + && !path.getFileName().toString().equals("module-info.java") + && path.getFileName().toString().endsWith(".java")) + .forEach(p -> { + assertThat(p) + .describedAs("unexpected plugin file location '%s' should be in '%s", p, expectedPrefix) + .startsWith(expectedPrefix); + + Path relativePath = expectedPrefix.relativize(p); + String subPackage = relativePath.getName(0).toString(); + if (null == pluginSubPackage.get()) { + pluginSubPackage.set(subPackage); + } else { + assertThat(subPackage).isEqualTo(pluginSubPackage.get()); + } + }); + } catch (IOException e) { + throw new IllegalStateException(e); + } + + assertThat(pluginSubPackage.get()).isNotNull(); + return String.format("co.elastic.apm.agent.%s", pluginSubPackage.get()); + } + + private static Path getModuleRoot() { + Path classLocation; + try { + classLocation = Paths.get(PackagingTest.class.getProtectionDomain().getCodeSource().getLocation().toURI()); + } catch (URISyntaxException e) { + throw new IllegalStateException(e); + } + Path moduleRoot = classLocation.getParent().getParent(); + assertThat(moduleRoot).isDirectory(); + return moduleRoot; + } + + private static void checkContainsPluginAsDependencies(Path pomPath, Set plugins) { + assertThat(pomPath).isRegularFile(); + + Model model = parseMaven(pomPath); + Set dependenciesArtifacts = model.getDependencies().stream() + .filter(d -> sameProjectGroup(model, d) && sameProjectVersion(model, d)) + .map(Dependency::getArtifactId) + .collect(Collectors.toSet()); + + assertThat(dependenciesArtifacts).describedAs(createDescription(plugins)).containsAll(plugins); + + } + private static String createDescription(Set plugins) { StringBuilder sb = new StringBuilder();