Skip to content

Commit 729372b

Browse files
authored
Spring6 webflux tests (#3114)
1 parent 9ab0b3f commit 729372b

File tree

26 files changed

+465
-131
lines changed

26 files changed

+465
-131
lines changed
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
* specific language governing permissions and limitations
1717
* under the License.
1818
*/
19-
package co.elastic.apm.agent.springwebmvc;
19+
package co.elastic.apm.agent.testutils;
2020

2121
import org.junit.jupiter.api.Test;
2222
import org.junit.jupiter.api.condition.EnabledForJreRange;

apm-agent-plugins/apm-spring-webflux/apm-spring-webclient-plugin/pom.xml

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,18 @@
2020
<animal.sniffer.skip>true</animal.sniffer.skip>
2121
</properties>
2222

23+
<dependencyManagement>
24+
<dependencies>
25+
<dependency>
26+
<groupId>org.springframework.boot</groupId>
27+
<artifactId>spring-boot-dependencies</artifactId>
28+
<version>${version.spring-boot-3}</version>
29+
<type>pom</type>
30+
<scope>import</scope>
31+
</dependency>
32+
</dependencies>
33+
</dependencyManagement>
34+
2335
<dependencies>
2436
<dependency>
2537
<groupId>co.elastic.apm</groupId>
@@ -29,13 +41,23 @@
2941
<dependency>
3042
<groupId>org.springframework</groupId>
3143
<artifactId>spring-web</artifactId>
32-
<version>${version.spring}</version>
3344
<scope>provided</scope>
45+
<exclusions>
46+
<exclusion>
47+
<!--
48+
jcl needs to be excluded because of the integration tests.
49+
jcl seems to provide apache commons logging classes, but compiled with java 17.
50+
Wiremock does some reflective lookup on these classes, which causes the tests to fail with Java 11.
51+
As a workaround, jcl has been excluded and commons-logging has been included as test dependency instead
52+
-->
53+
<groupId>org.springframework</groupId>
54+
<artifactId>spring-jcl</artifactId>
55+
</exclusion>
56+
</exclusions>
3457
</dependency>
3558
<dependency>
3659
<groupId>org.springframework</groupId>
3760
<artifactId>spring-webflux</artifactId>
38-
<version>${version.spring}</version>
3961
<scope>provided</scope>
4062
</dependency>
4163

@@ -51,27 +73,40 @@
5173
<dependency>
5274
<groupId>io.projectreactor.netty</groupId>
5375
<artifactId>reactor-netty-http</artifactId>
54-
<version>1.0.7</version>
76+
<scope>test</scope>
77+
</dependency>
78+
<dependency>
79+
<groupId>io.netty</groupId>
80+
<artifactId>netty-resolver-dns-native-macos</artifactId>
81+
<classifier>osx-aarch_64</classifier>
5582
<scope>test</scope>
5683
</dependency>
5784
<!-- alternative client implementation: jetty -->
5885
<dependency>
5986
<groupId>org.eclipse.jetty</groupId>
6087
<artifactId>jetty-reactive-httpclient</artifactId>
61-
<version>1.1.11</version>
6288
<scope>test</scope>
6389
</dependency>
6490
<!-- alternative client implementation: apache async client -->
6591
<dependency>
6692
<groupId>org.apache.httpcomponents.client5</groupId>
6793
<artifactId>httpclient5</artifactId>
68-
<version>5.1.3</version>
6994
<scope>test</scope>
7095
</dependency>
7196
<dependency>
7297
<groupId>org.apache.httpcomponents.core5</groupId>
7398
<artifactId>httpcore5-reactive</artifactId>
74-
<version>5.1.3</version>
99+
<scope>test</scope>
100+
</dependency>
101+
<dependency>
102+
<groupId>org.apache.ivy</groupId>
103+
<artifactId>ivy</artifactId>
104+
<scope>test</scope>
105+
</dependency>
106+
<dependency>
107+
<groupId>commons-logging</groupId>
108+
<artifactId>commons-logging</artifactId>
109+
<version>1.2</version>
75110
<scope>test</scope>
76111
</dependency>
77112
</dependencies>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*
2+
* Licensed to Elasticsearch B.V. under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch B.V. licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package co.elastic.apm.agent.springwebclient;
20+
21+
import co.elastic.apm.agent.httpclient.AbstractHttpClientInstrumentationTest;
22+
import co.elastic.apm.agent.testutils.JUnit4TestClassWithDependencyRunner;
23+
import org.junit.jupiter.params.ParameterizedTest;
24+
import org.junit.jupiter.params.provider.ValueSource;
25+
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
26+
import org.springframework.web.reactive.function.client.WebClient;
27+
import reactor.netty.http.client.HttpClient;
28+
29+
import java.util.Arrays;
30+
import java.util.List;
31+
32+
public class WebClientInstrumentationIT {
33+
34+
35+
@ParameterizedTest
36+
@ValueSource(strings = {"5.3.26"})
37+
public void testNetty(String springVersion) throws Exception {
38+
List<String> dependencies = Arrays.asList(
39+
"org.springframework:spring-web:" + springVersion,
40+
"org.springframework:spring-webflux:" + springVersion,
41+
"org.springframework:spring-core:" + springVersion,
42+
"org.springframework:spring-beans:" + springVersion
43+
);
44+
JUnit4TestClassWithDependencyRunner runner = new JUnit4TestClassWithDependencyRunner(dependencies, WebClientInstrumentationIT.class.getName() + "$TestImpl", WebClientInstrumentationIT.class.getName());
45+
runner.run();
46+
}
47+
48+
/**
49+
* We don't test with all variations of {@link WebClientInstrumentationTest}
50+
* but just with netty for integration test.
51+
*/
52+
public static class TestImpl extends AbstractHttpClientInstrumentationTest {
53+
54+
private final WebClient webClient;
55+
56+
57+
public TestImpl() {
58+
HttpClient httpClient = HttpClient.create()
59+
// followRedirect(boolean) only enables redirect for 30[1278], not 303
60+
.followRedirect((req, res) -> res.status().code() == 303);
61+
62+
// crete netty reactor client
63+
webClient = WebClient.builder()
64+
.clientConnector(new ReactorClientHttpConnector(httpClient))
65+
.build();
66+
}
67+
68+
@Override
69+
public boolean isRequireCheckErrorWhenCircularRedirect() {
70+
// circular redirect does not trigger an error to capture with netty
71+
return false;
72+
}
73+
74+
@Override
75+
public boolean isTestHttpCallWithUserInfoEnabled() {
76+
// user info URI does not work with netty
77+
return false;
78+
}
79+
80+
81+
@Override
82+
protected void performGet(String path) throws Exception {
83+
webClient.get().uri(path).exchangeToMono(response -> response.bodyToMono(String.class)).block();
84+
}
85+
}
86+
}

apm-agent-plugins/apm-spring-webflux/apm-spring-webclient-plugin/src/test/java/co/elastic/apm/agent/springwebclient/WebClientInstrumentationTest.java

Lines changed: 57 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
*/
1919
package co.elastic.apm.agent.springwebclient;
2020

21+
import co.elastic.apm.agent.common.JvmRuntimeInfo;
2122
import co.elastic.apm.agent.httpclient.AbstractHttpClientInstrumentationTest;
2223
import org.junit.runner.RunWith;
2324
import org.junit.runners.Parameterized;
@@ -30,34 +31,41 @@
3031
@RunWith(Parameterized.class)
3132
public class WebClientInstrumentationTest extends AbstractHttpClientInstrumentationTest {
3233

33-
private final WebClient webClient;
34+
/**
35+
* Can't directly reference WebClient because it is compiled with java 17.
36+
*/
37+
private final Object webClient;
3438

3539
private final RequestStrategy strategy;
3640

3741
private final boolean isNetty;
3842

39-
public WebClientInstrumentationTest(String clientIgnored, WebClient webClient, RequestStrategy strategy, boolean isNetty) {
43+
public WebClientInstrumentationTest(String clientIgnored, Object webClient, RequestStrategy strategy, boolean isNetty) {
4044
this.webClient = webClient;
4145
this.strategy = strategy;
4246
this.isNetty = isNetty;
4347
}
4448

4549
@Parameterized.Parameters(name = "client = {0}, request strategy = {2}")
4650
public static Object[][] testParams() {
47-
return new Object[][]{
48-
{"jetty", jettyClient(), RequestStrategy.EXCHANGE, false},
49-
{"jetty", jettyClient(), RequestStrategy.EXCHANGE_TO_FLUX, false},
50-
{"jetty", jettyClient(), RequestStrategy.EXCHANGE_TO_MONO, false},
51-
{"jetty", jettyClient(), RequestStrategy.RETRIEVE, false},
52-
{"netty", nettyClient(), RequestStrategy.EXCHANGE, true},
53-
{"netty", nettyClient(), RequestStrategy.EXCHANGE_TO_FLUX, true},
54-
{"netty", nettyClient(), RequestStrategy.EXCHANGE_TO_MONO, true},
55-
{"netty", nettyClient(), RequestStrategy.RETRIEVE, true},
56-
{"hc5", reactiveHttpClient5(), RequestStrategy.EXCHANGE, false},
57-
{"hc5", reactiveHttpClient5(), RequestStrategy.EXCHANGE_TO_FLUX, false},
58-
{"hc5", reactiveHttpClient5(), RequestStrategy.EXCHANGE_TO_MONO, false},
59-
{"hc5", reactiveHttpClient5(), RequestStrategy.RETRIEVE, false}
60-
};
51+
if (JvmRuntimeInfo.ofCurrentVM().getMajorVersion() >= 17) {
52+
return new Object[][]{
53+
{"jetty", Clients.jettyClient(), RequestStrategy.EXCHANGE, false},
54+
{"jetty", Clients.jettyClient(), RequestStrategy.EXCHANGE_TO_FLUX, false},
55+
{"jetty", Clients.jettyClient(), RequestStrategy.EXCHANGE_TO_MONO, false},
56+
{"jetty", Clients.jettyClient(), RequestStrategy.RETRIEVE, false},
57+
{"netty", Clients.nettyClient(), RequestStrategy.EXCHANGE, true},
58+
{"netty", Clients.nettyClient(), RequestStrategy.EXCHANGE_TO_FLUX, true},
59+
{"netty", Clients.nettyClient(), RequestStrategy.EXCHANGE_TO_MONO, true},
60+
{"netty", Clients.nettyClient(), RequestStrategy.RETRIEVE, true},
61+
{"hc5", Clients.reactiveHttpClient5(), RequestStrategy.EXCHANGE, false},
62+
{"hc5", Clients.reactiveHttpClient5(), RequestStrategy.EXCHANGE_TO_FLUX, false},
63+
{"hc5", Clients.reactiveHttpClient5(), RequestStrategy.EXCHANGE_TO_MONO, false},
64+
{"hc5", Clients.reactiveHttpClient5(), RequestStrategy.RETRIEVE, false}
65+
};
66+
} else {
67+
return new Object[0][0];
68+
}
6169
}
6270

6371
@Override
@@ -86,55 +94,58 @@ protected enum RequestStrategy {
8694
EXCHANGE {
8795
@Override
8896
@SuppressWarnings("deprecation")
89-
void execute(WebClient client, String uri) {
90-
client.get().uri(uri).exchange() // deprecated API
97+
void execute(Object client, String uri) {
98+
((WebClient) client).get().uri(uri).exchange() // deprecated API
9199
.block();
92100
}
93101
},
94102
EXCHANGE_TO_FLUX {
95103
@Override
96-
void execute(WebClient client, String uri) {
97-
client.get().uri(uri).exchangeToFlux(response -> response.bodyToFlux(String.class)).blockLast();
104+
void execute(Object client, String uri) {
105+
((WebClient) client).get().uri(uri).exchangeToFlux(response -> response.bodyToFlux(String.class)).blockLast();
98106
}
99107
},
100108
EXCHANGE_TO_MONO {
101109
// TODO
102110
@Override
103-
void execute(WebClient client, String uri) {
104-
client.get().uri(uri).exchangeToMono(response -> response.bodyToMono(String.class)).block();
111+
void execute(Object client, String uri) {
112+
((WebClient) client).get().uri(uri).exchangeToMono(response -> response.bodyToMono(String.class)).block();
105113
}
106114
},
107115
RETRIEVE {
108116
@Override
109-
void execute(WebClient client, String uri) {
110-
client.get().uri(uri).retrieve().bodyToMono(String.class).block();
117+
void execute(Object client, String uri) {
118+
((WebClient) client).get().uri(uri).retrieve().bodyToMono(String.class).block();
111119
}
112120
};
113121

114-
abstract void execute(WebClient client, String uri);
115-
}
116-
117-
private static WebClient jettyClient() {
118-
return WebClient.builder()
119-
.clientConnector(new JettyClientHttpConnector())
120-
.build();
121-
}
122-
123-
private static WebClient nettyClient() {
124-
HttpClient httpClient = HttpClient.create()
125-
// followRedirect(boolean) only enables redirect for 30[1278], not 303
126-
.followRedirect((req, res) -> res.status().code() == 303);
127-
128-
// crete netty reactor client
129-
return WebClient.builder()
130-
.clientConnector(new ReactorClientHttpConnector(httpClient))
131-
.build();
122+
abstract void execute(Object client, String uri);
132123
}
133124

134-
public static WebClient reactiveHttpClient5() {
135-
return WebClient.builder()
136-
.clientConnector(new HttpComponentsClientHttpConnector())
137-
.build();
125+
public static class Clients {
126+
127+
private static Object jettyClient() {
128+
return WebClient.builder()
129+
.clientConnector(new JettyClientHttpConnector())
130+
.build();
131+
}
132+
133+
private static Object nettyClient() {
134+
HttpClient httpClient = HttpClient.create()
135+
// followRedirect(boolean) only enables redirect for 30[1278], not 303
136+
.followRedirect((req, res) -> res.status().code() == 303);
137+
138+
// crete netty reactor client
139+
return WebClient.builder()
140+
.clientConnector(new ReactorClientHttpConnector(httpClient))
141+
.build();
142+
}
143+
144+
public static Object reactiveHttpClient5() {
145+
return WebClient.builder()
146+
.clientConnector(new HttpComponentsClientHttpConnector())
147+
.build();
148+
}
138149
}
139150

140151
}

0 commit comments

Comments
 (0)