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 super MethodDescription> 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 super MethodDescription> 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();