Move helper class to spring package so that loadClass can find it (#3718)

* Move helper class to spring package so that loadClass can find it

* spotless

* Add tests

* Add comments

* remove unneeded dependency

* comments
This commit is contained in:
Lauri Tulmin 2021-08-20 22:31:25 +03:00 committed by GitHub
parent 8d90462a28
commit 38c8f8940c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 498 additions and 37 deletions

View File

@ -0,0 +1,18 @@
plugins {
id("otel.javaagent-instrumentation")
}
muzzle {
pass {
group.set("org.springframework")
module.set("spring-web")
versions.set("[3.1.0.RELEASE,]")
// these versions depend on javax.faces:jsf-api:1.1 which was released as pom only
skip("1.2.1", "1.2.2", "1.2.3", "1.2.4")
assertInverse.set(true)
}
}
dependencies {
compileOnly("org.springframework:spring-web:3.1.0.RELEASE")
}

View File

@ -0,0 +1,33 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.springweb;
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
import static java.util.Collections.singletonList;
import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import java.util.List;
import net.bytebuddy.matcher.ElementMatcher;
@AutoService(InstrumentationModule.class)
public class SpringWebInstrumentationModule extends InstrumentationModule {
public SpringWebInstrumentationModule() {
super("spring-web", "spring-web-3.1");
}
@Override
public ElementMatcher.Junction<ClassLoader> classLoaderMatcher() {
// class added in 3.1
return hasClassesNamed("org.springframework.web.method.HandlerMethod");
}
@Override
public List<TypeInstrumentation> typeInstrumentations() {
return singletonList(new WebApplicationContextInstrumentation());
}
}

View File

@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.springwebmvc;
package io.opentelemetry.javaagent.instrumentation.springweb;
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.extendsClass;
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
@ -11,6 +11,7 @@ import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import static org.springframework.beans.factory.config.BeanDefinition.SCOPE_SINGLETON;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
@ -19,9 +20,10 @@ import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
/**
* This instrumentation adds the HandlerMappingResourceNameFilter definition to the spring context
* This instrumentation adds the OpenTelemetryHandlerMappingFilter definition to the spring context
* When the context is created, the filter will be added to the beginning of the filter chain.
*/
public class WebApplicationContextInstrumentation implements TypeInstrumentation {
@ -59,10 +61,29 @@ public class WebApplicationContextInstrumentation implements TypeInstrumentation
public static void onEnter(@Advice.Argument(0) ConfigurableListableBeanFactory beanFactory) {
if (beanFactory instanceof BeanDefinitionRegistry
&& !beanFactory.containsBean("otelAutoDispatcherFilter")) {
try {
// Firstly check whether DispatcherServlet is present. We need to load an instrumented
// class from spring-webmvc to trigger injection that makes
// OpenTelemetryHandlerMappingFilter available.
beanFactory
.getBeanClassLoader()
.loadClass("org.springframework.web.servlet.DispatcherServlet");
((BeanDefinitionRegistry) beanFactory)
.registerBeanDefinition(
"otelAutoDispatcherFilter", new HandlerMappingResourceNameFilter.BeanDefinition());
// Now attempt to load our injected instrumentation class.
Class<?> clazz =
beanFactory
.getBeanClassLoader()
.loadClass("org.springframework.web.servlet.OpenTelemetryHandlerMappingFilter");
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setScope(SCOPE_SINGLETON);
beanDefinition.setBeanClass(clazz);
beanDefinition.setBeanClassName(clazz.getName());
((BeanDefinitionRegistry) beanFactory)
.registerBeanDefinition("otelAutoDispatcherFilter", beanDefinition);
} catch (ClassNotFoundException ignored) {
// Ignore
}
}
}
}

View File

@ -15,17 +15,6 @@ muzzle {
extraDependency("javax.servlet:javax.servlet-api:3.0.1")
assertInverse.set(true)
}
// FIXME: webmvc depends on web, so we need a separate instrumentation for spring-web specifically.
fail {
group.set("org.springframework")
module.set("spring-web")
versions.set("[,]")
// these versions depend on org.springframework:spring-web which has a bad dependency on
// javax.faces:jsf-api:1.1 which was released as pom only
skip("1.2.1", "1.2.2", "1.2.3", "1.2.4")
extraDependency("javax.servlet:javax.servlet-api:3.0.1")
}
}
val versions: Map<String, String> by project
@ -36,12 +25,11 @@ dependencies {
// compileOnly("org.springframework:spring-webmvc:2.5.6")
// compileOnly("javax.servlet:servlet-api:2.4")
testImplementation(project(":testing-common"))
// Include servlet instrumentation for verifying the tomcat requests
testInstrumentation(project(":instrumentation:servlet:servlet-3.0:javaagent"))
testInstrumentation(project(":instrumentation:servlet:servlet-javax-common:javaagent"))
testInstrumentation(project(":instrumentation:tomcat:tomcat-7.0:javaagent"))
testInstrumentation(project(":instrumentation:spring:spring-web-3.1:javaagent"))
testImplementation("javax.validation:validation-api:1.1.0.Final")
testImplementation("org.hibernate:hibernate-validator:5.4.2.Final")

View File

@ -24,6 +24,7 @@ import net.bytebuddy.matcher.ElementMatcher;
import org.springframework.context.ApplicationContext;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.OpenTelemetryHandlerMappingFilter;
public class DispatcherServletInstrumentation implements TypeInstrumentation {
@ -62,8 +63,8 @@ public class DispatcherServletInstrumentation implements TypeInstrumentation {
@Advice.Argument(0) ApplicationContext springCtx,
@Advice.FieldValue("handlerMappings") List<HandlerMapping> handlerMappings) {
if (springCtx.containsBean("otelAutoDispatcherFilter")) {
HandlerMappingResourceNameFilter filter =
(HandlerMappingResourceNameFilter) springCtx.getBean("otelAutoDispatcherFilter");
OpenTelemetryHandlerMappingFilter filter =
(OpenTelemetryHandlerMappingFilter) springCtx.getBean("otelAutoDispatcherFilter");
if (handlerMappings != null && filter != null) {
filter.setHandlerMappings(handlerMappings);
}

View File

@ -18,11 +18,14 @@ public class SpringWebMvcInstrumentationModule extends InstrumentationModule {
super("spring-webmvc", "spring-webmvc-3.1");
}
@Override
public boolean isHelperClass(String className) {
return className.startsWith(
"org.springframework.web.servlet.OpenTelemetryHandlerMappingFilter");
}
@Override
public List<TypeInstrumentation> typeInstrumentations() {
return asList(
new WebApplicationContextInstrumentation(),
new DispatcherServletInstrumentation(),
new HandlerAdapterInstrumentation());
return asList(new DispatcherServletInstrumentation(), new HandlerAdapterInstrumentation());
}
}

View File

@ -3,12 +3,13 @@
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.springwebmvc;
package org.springframework.web.servlet;
import static io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming.Source.CONTROLLER;
import io.opentelemetry.context.Context;
import io.opentelemetry.instrumentation.api.servlet.ServerSpanNaming;
import io.opentelemetry.javaagent.instrumentation.springwebmvc.SpringWebMvcServerSpanNaming;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@ -20,13 +21,10 @@ import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.core.Ordered;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
public class HandlerMappingResourceNameFilter implements Filter, Ordered {
public class OpenTelemetryHandlerMappingFilter implements Filter, Ordered {
private volatile List<HandlerMapping> handlerMappings;
@Override
@ -122,12 +120,4 @@ public class HandlerMappingResourceNameFilter implements Filter, Ordered {
// Run after all HIGHEST_PRECEDENCE items
return Ordered.HIGHEST_PRECEDENCE + 1;
}
public static class BeanDefinition extends GenericBeanDefinition {
public BeanDefinition() {
setScope(SCOPE_SINGLETON);
setBeanClass(HandlerMappingResourceNameFilter.class);
setBeanClassName(HandlerMappingResourceNameFilter.class.getName());
}
}
}

View File

@ -0,0 +1,82 @@
plugins {
id("otel.javaagent-testing")
}
val testServer by configurations.creating
val appLibrary by configurations.creating
configurations.named("testCompileOnly") {
extendsFrom(appLibrary)
}
dependencies {
appLibrary("org.springframework:spring-webmvc:3.1.0.RELEASE")
testImplementation("javax.servlet:javax.servlet-api:3.1.0")
val arquillianVersion = "1.4.0.Final"
testImplementation("org.jboss.arquillian.junit:arquillian-junit-container:${arquillianVersion}")
testImplementation("org.jboss.arquillian.protocol:arquillian-protocol-servlet:${arquillianVersion}")
testImplementation("org.jboss.arquillian.spock:arquillian-spock-container:1.0.0.CR1")
testImplementation("org.jboss.shrinkwrap:shrinkwrap-impl-base:1.2.6")
testRuntimeOnly("org.wildfly.arquillian:wildfly-arquillian-container-embedded:2.2.0.Final")
testInstrumentation(project(":instrumentation:servlet:servlet-3.0:javaagent"))
testInstrumentation(project(":instrumentation:spring:spring-webmvc-3.1:javaagent"))
testInstrumentation(project(":instrumentation:spring:spring-web-3.1:javaagent"))
// wildfly version used to run tests
testServer("org.wildfly:wildfly-dist:18.0.0.Final@zip")
}
tasks {
// extract wildfly dist, path is used from arquillian.xml
val setupServer by registering(Copy::class) {
from(zipTree(testServer.singleFile))
into(file("build/server/"))
}
// logback-classic contains /META-INF/services/javax.servlet.ServletContainerInitializer
// that breaks deploy on embedded wildfly
// create a copy of logback-classic jar that does not have this file
val modifyLogbackJar by registering(Jar::class) {
destinationDirectory.set(file("$buildDir/tmp"))
archiveFileName.set("logback-classic-modified.jar")
exclude("/META-INF/services/javax.servlet.ServletContainerInitializer")
doFirst {
configurations.configureEach {
if (name.toLowerCase().endsWith("testruntimeclasspath")) {
val logbackJar = find { it.name.contains("logback-classic") }
from(zipTree(logbackJar))
}
}
}
}
val copyDependencies by registering(Copy::class) {
// test looks for spring jars that are bundled inside deployed application from this directory
from(appLibrary).into("$buildDir/app-libs")
}
named<Test>("test") {
dependsOn(modifyLogbackJar)
dependsOn(setupServer)
dependsOn(copyDependencies)
doFirst {
// --add-modules is unrecognized on jdk8, ignore it instead of failing
jvmArgs("-XX:+IgnoreUnrecognizedVMOptions")
// needed for java 11 to avoid org.jboss.modules.ModuleNotFoundException: java.se
jvmArgs("--add-modules=java.se")
// add offset to default port values
jvmArgs("-Djboss.socket.binding.port-offset=300")
// remove logback-classic from classpath
classpath = classpath.filter {
!it.absolutePath.contains("logback-classic")
}
// add modified copy of logback-classic to classpath
classpath = classpath.plus(files("$buildDir/tmp/logback-classic-modified.jar"))
}
}
}

View File

@ -0,0 +1,165 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
import static io.opentelemetry.api.trace.SpanKind.INTERNAL
import static io.opentelemetry.api.trace.SpanKind.SERVER
import com.example.hello.HelloController
import com.example.hello.TestFilter
import io.opentelemetry.api.trace.StatusCode
import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes
import io.opentelemetry.testing.internal.armeria.client.WebClient
import io.opentelemetry.testing.internal.armeria.common.AggregatedHttpResponse
import org.jboss.arquillian.container.test.api.Deployment
import org.jboss.arquillian.container.test.api.RunAsClient
import org.jboss.arquillian.spock.ArquillianSputnik
import org.jboss.arquillian.test.api.ArquillianResource
import org.jboss.shrinkwrap.api.Archive
import org.jboss.shrinkwrap.api.ShrinkWrap
import org.jboss.shrinkwrap.api.spec.EnterpriseArchive
import org.jboss.shrinkwrap.api.spec.WebArchive
import org.junit.runner.RunWith
@RunWith(ArquillianSputnik)
@RunAsClient
// Test that OpenTelemetryHandlerMappingFilter injection works when spring libraries are in various
// locations inside deployment.
abstract class OpenTelemetryHandlerMappingFilterTest extends AgentInstrumentationSpecification {
static WebClient client = WebClient.of()
@ArquillianResource
static URI url
def getAddress(String service) {
return url.resolve(service).toString()
}
def "test success"() {
when:
AggregatedHttpResponse response = client.get(getAddress("hello/world")).aggregate().join()
then:
response.status().code() == 200
response.contentUtf8() == "hello world"
and:
assertTraces(1) {
trace(0, 2) {
span(0) {
name "/hello/{name}"
kind SERVER
hasNoParent()
}
span(1) {
name "HelloController.hello"
kind INTERNAL
childOf(span(0))
}
}
}
}
def "test exception"() {
when:
AggregatedHttpResponse response = client.get(getAddress("hello/exception")).aggregate().join()
then:
response.status().code() == 500
and:
assertTraces(1) {
trace(0, 1) {
span(0) {
name "/hello/{name}"
kind SERVER
status StatusCode.ERROR
hasNoParent()
event(0) {
eventName(SemanticAttributes.EXCEPTION_EVENT_NAME)
attributes {
"${SemanticAttributes.EXCEPTION_TYPE.key}" "javax.servlet.ServletException"
"${SemanticAttributes.EXCEPTION_MESSAGE.key}" "exception"
"${SemanticAttributes.EXCEPTION_STACKTRACE.key}" { it == null || it instanceof String }
}
}
}
}
}
}
}
// spring is inside ear/lib
class LibsInEarTest extends OpenTelemetryHandlerMappingFilterTest {
@Deployment
static Archive<?> createDeployment() {
WebArchive war = ShrinkWrap.create(WebArchive, "test.war")
.addAsWebInfResource("web.xml")
.addAsWebInfResource("dispatcher-servlet.xml")
.addAsWebInfResource("applicationContext.xml")
.addClass(HelloController)
.addClass(TestFilter)
EnterpriseArchive ear = ShrinkWrap.create(EnterpriseArchive)
.setApplicationXML("application.xml")
.addAsModule(war)
.addAsLibraries(new File("build/app-libs").listFiles())
return ear
}
}
// spring is inside war/WEB-INF/lib
class LibsInWarTest extends OpenTelemetryHandlerMappingFilterTest {
@Deployment
static Archive<?> createDeployment() {
WebArchive war = ShrinkWrap.create(WebArchive, "test.war")
.addAsWebInfResource("web.xml")
.addAsWebInfResource("dispatcher-servlet.xml")
.addAsWebInfResource("applicationContext.xml")
.addClass(HelloController)
.addClass(TestFilter)
.addAsLibraries(new File("build/app-libs").listFiles())
EnterpriseArchive ear = ShrinkWrap.create(EnterpriseArchive)
.setApplicationXML("application.xml")
.addAsModule(war)
return ear
}
}
// Everything except spring-webmvc is in ear/lib, spring-webmvc is in war/WEB-INF/lib
class MixedLibsTest extends OpenTelemetryHandlerMappingFilterTest {
@Deployment
static Archive<?> createDeployment() {
WebArchive war = ShrinkWrap.create(WebArchive, "test.war")
.addAsWebInfResource("web.xml")
.addAsWebInfResource("dispatcher-servlet.xml")
.addAsWebInfResource("applicationContext.xml")
.addClass(HelloController)
.addClass(TestFilter)
.addAsLibraries(new File("build/app-libs").listFiles(new FilenameFilter() {
@Override
boolean accept(File dir, String name) {
return name.contains("spring-webmvc")
}
}))
EnterpriseArchive ear = ShrinkWrap.create(EnterpriseArchive)
.setApplicationXML("application.xml")
.addAsModule(war)
.addAsLibraries(new File("build/app-libs").listFiles(new FilenameFilter() {
@Override
boolean accept(File dir, String name) {
return !name.contains("spring-webmvc")
}
}))
return ear
}
}

View File

@ -0,0 +1,21 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package com.example.hello;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class HelloController {
@RequestMapping("/hello/{name}")
@ResponseBody
public String hello(@PathVariable("name") String name) {
return "hello " + name;
}
}

View File

@ -0,0 +1,41 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package com.example.hello;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Component;
@Component("testFilter")
public class TestFilter implements Filter {
public TestFilter() {}
@Override
public void init(FilterConfig filterConfig) {}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
// To test OpenTelemetryHandlerMappingFilter we need to stop the request before it reaches
// HandlerAdapter which gives server span the same name as OpenTelemetryHandlerMappingFilter.
// Throwing an exception from servlet filter works for that.
if (httpServletRequest.getRequestURI().contains("exception")) {
throw new ServletException("exception");
}
chain.doFilter(request, response);
}
@Override
public void destroy() {}
}

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<application xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/application_7.xsd" version="7">
<display-name>hello-ear</display-name>
<module>
<web>
<web-uri>test.war</web-uri>
<context-root>/</context-root>
</web>
</module>
<library-directory>lib</library-directory>
</application>

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd">
<mvc:annotation-driven/>
<context:component-scan base-package="com.example.hello"/>
</beans>

View File

@ -0,0 +1,15 @@
<arquillian xmlns="http://jboss.org/schema/arquillian"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://jboss.org/schema/arquillian
http://jboss.org/schema/arquillian/arquillian_1_0.xsd">
<defaultProtocol type="Servlet 3.0" />
<container qualifier="wildfly-embedded" default="true">
<configuration>
<property name="jbossHome">build/server/wildfly-18.0.0.Final</property>
<property name="modulePath">build/server/wildfly-18.0.0.Final/modules</property>
</configuration>
</container>
</arquillian>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd">
</beans>

View File

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<!--
Spring boot automatically registers a servlet filter when spring bean implements
javax.servlet.Filter but vanilla spring does not so we need to add a placeholder
for our servlet filter here.
-->
<filter>
<filter-name>otelAutoDispatcherFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter>
<filter-name>testFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<filter-mapping>
<filter-name>otelAutoDispatcherFilter</filter-name>
<servlet-name>dispatcher</servlet-name>
</filter-mapping>
<filter-mapping>
<filter-name>testFilter</filter-name>
<servlet-name>dispatcher</servlet-name>
</filter-mapping>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>

View File

@ -306,10 +306,12 @@ include(":instrumentation:spring:spring-integration-4.1:library")
include(":instrumentation:spring:spring-integration-4.1:testing")
include(":instrumentation:spring:spring-rabbit-1.0:javaagent")
include(":instrumentation:spring:spring-scheduling-3.1:javaagent")
include(":instrumentation:spring:spring-web-3.1:javaagent")
include(":instrumentation:spring:spring-web-3.1:library")
include(":instrumentation:spring:spring-web-3.1:testing")
include(":instrumentation:spring:spring-webmvc-3.1:javaagent")
include(":instrumentation:spring:spring-webmvc-3.1:library")
include(":instrumentation:spring:spring-webmvc-3.1:wildfly-testing")
include(":instrumentation:spring:spring-webflux-5.0:javaagent")
include(":instrumentation:spring:spring-webflux-5.0:library")
include(":instrumentation:spring:spring-ws-2.0:javaagent")