Skip to content

Commit

Permalink
More type pollution tests + avoid some of the checks (#11495)
Browse files Browse the repository at this point in the history
  • Loading branch information
dstepanov authored Jan 13, 2025
1 parent d0ef5f9 commit 7cf4f31
Show file tree
Hide file tree
Showing 4 changed files with 280 additions and 32 deletions.
1 change: 1 addition & 0 deletions benchmarks/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ dependencies {
api(projects.micronautJacksonDatabind)
api(projects.micronautRouter)
api(projects.micronautRuntime)
api(projects.micronautCoreReactive)

api(platform(libs.test.boms.micronaut.validation))
api(libs.managed.reactor)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import io.micronaut.context.ApplicationContext;
import io.micronaut.context.annotation.Requires;
import io.micronaut.core.annotation.Nullable;
import io.micronaut.core.async.annotation.SingleResult;
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
Expand All @@ -22,6 +23,7 @@
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import jakarta.inject.Inject;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Assertions;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Mode;
Expand All @@ -31,14 +33,19 @@
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.TearDown;
import org.openjdk.jmh.infra.Blackhole;
import org.openjdk.jmh.profile.AsyncProfiler;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.TimeUnit;

/**
Expand Down Expand Up @@ -122,7 +129,7 @@ public enum Request {
TFB_LIKE {
@Override
FullHttpRequest request() {
FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/tfblike");
FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/tfblike/bytes");
request.headers().add(HttpHeaderNames.ACCEPT, "text/plain,text/html;q=0.9,application/xhtml+xml;q=0.9,application/xml;q=0.8,*/*;q=0.7");
return request;
}
Expand All @@ -136,6 +143,150 @@ void verifyResponse(FullHttpResponse response) {
Assertions.assertEquals(expectedResponseBody.length(), response.headers().getInt(HttpHeaderNames.CONTENT_LENGTH));
}
},
TFB_STRING {
@Override
FullHttpRequest request() {
FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/tfblike/string");
request.headers().add(HttpHeaderNames.ACCEPT, "text/plain,text/html;q=0.9,application/xhtml+xml;q=0.9,application/xml;q=0.8,*/*;q=0.7");
return request;
}

@Override
void verifyResponse(FullHttpResponse response) {
Assertions.assertEquals(HttpResponseStatus.OK, response.status());
Assertions.assertEquals("text/plain", response.headers().get(HttpHeaderNames.CONTENT_TYPE));
String expectedResponseBody = "Hello, World!";
Assertions.assertEquals(expectedResponseBody, response.content().toString(StandardCharsets.UTF_8));
Assertions.assertEquals(expectedResponseBody.length(), response.headers().getInt(HttpHeaderNames.CONTENT_LENGTH));
}
},
TFB_LIKE_BEANS1 {
@Override
FullHttpRequest request() {
FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/tfblike/beans1");
request.headers().add(HttpHeaderNames.ACCEPT, "application/json");
return request;
}

@Override
void verifyResponse(FullHttpResponse response) {
Assertions.assertEquals(HttpResponseStatus.OK, response.status());
Assertions.assertEquals("application/json", response.headers().get(HttpHeaderNames.CONTENT_TYPE));
String expectedResponseBody = """
[{"id":1,"message":"A"},{"id":2,"message":"B"},{"id":3,"message":"C"}]""";
Assertions.assertEquals(expectedResponseBody, response.content().toString(StandardCharsets.UTF_8));
Assertions.assertEquals(expectedResponseBody.length(), response.headers().getInt(HttpHeaderNames.CONTENT_LENGTH));
}
},
TFB_LIKE_BEANS2 {
@Override
FullHttpRequest request() {
FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/tfblike/beans2");
request.headers().add(HttpHeaderNames.ACCEPT, "application/json");
return request;
}

@Override
void verifyResponse(FullHttpResponse response) {
Assertions.assertEquals(HttpResponseStatus.OK, response.status());
Assertions.assertEquals("application/json", response.headers().get(HttpHeaderNames.CONTENT_TYPE));
String expectedResponseBody = """
[{"id":1,"randomNumber":123},{"id":2,"randomNumber":456},{"id":3,"randomNumber":789}]""";
Assertions.assertEquals(expectedResponseBody, response.content().toString(StandardCharsets.UTF_8));
Assertions.assertEquals(expectedResponseBody.length(), response.headers().getInt(HttpHeaderNames.CONTENT_LENGTH));
}
},
TFB_LIKE_ASYNC_BEANS1 {
@Override
FullHttpRequest request() {
FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/tfblike/async/beans1");
request.headers().add(HttpHeaderNames.ACCEPT, "application/json");
return request;
}

@Override
void verifyResponse(FullHttpResponse response) {
Assertions.assertEquals(HttpResponseStatus.OK, response.status());
Assertions.assertEquals("application/json", response.headers().get(HttpHeaderNames.CONTENT_TYPE));
String expectedResponseBody = """
[{"id":1,"message":"A"},{"id":2,"message":"B"},{"id":3,"message":"C"}]""";
Assertions.assertEquals(expectedResponseBody, response.content().toString(StandardCharsets.UTF_8));
Assertions.assertEquals(expectedResponseBody.length(), response.headers().getInt(HttpHeaderNames.CONTENT_LENGTH));
}
},
TFB_LIKE_ASYNC_BEANS2 {
@Override
FullHttpRequest request() {
FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/tfblike/async/beans2");
request.headers().add(HttpHeaderNames.ACCEPT, "application/json");
return request;
}

@Override
void verifyResponse(FullHttpResponse response) {
Assertions.assertEquals(HttpResponseStatus.OK, response.status());
Assertions.assertEquals("application/json", response.headers().get(HttpHeaderNames.CONTENT_TYPE));
String expectedResponseBody = """
[{"id":1,"randomNumber":123},{"id":2,"randomNumber":456},{"id":3,"randomNumber":789}]""";
Assertions.assertEquals(expectedResponseBody, response.content().toString(StandardCharsets.UTF_8));
Assertions.assertEquals(expectedResponseBody.length(), response.headers().getInt(HttpHeaderNames.CONTENT_LENGTH));
}
},
// Type pollution because of the Reactor
// TFB_LIKE_REACTIVE_BEANS1 {
// @Override
// FullHttpRequest request() {
// FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/tfblike/reactive/beans1");
// request.headers().add(HttpHeaderNames.ACCEPT, "application/json");
// return request;
// }
//
// @Override
// void verifyResponse(FullHttpResponse response) {
// Assertions.assertEquals(HttpResponseStatus.OK, response.status());
// Assertions.assertEquals("application/json", response.headers().get(HttpHeaderNames.CONTENT_TYPE));
// String expectedResponseBody = """
//[{"id":1,"message":"A"},{"id":2,"message":"B"},{"id":3,"message":"C"}]""";
// Assertions.assertEquals(expectedResponseBody, response.content().toString(StandardCharsets.UTF_8));
// Assertions.assertEquals(expectedResponseBody.length(), response.headers().getInt(HttpHeaderNames.CONTENT_LENGTH));
// }
// },
// TFB_LIKE_REACTIVE_BEANS2 {
// @Override
// FullHttpRequest request() {
// FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/tfblike/reactive/beans2");
// request.headers().add(HttpHeaderNames.ACCEPT, "application/json");
// return request;
// }
//
// @Override
// void verifyResponse(FullHttpResponse response) {
// Assertions.assertEquals(HttpResponseStatus.OK, response.status());
// Assertions.assertEquals("application/json", response.headers().get(HttpHeaderNames.CONTENT_TYPE));
// String expectedResponseBody = """
//[{"id":1,"randomNumber":123},{"id":2,"randomNumber":456},{"id":3,"randomNumber":789}]""";
// Assertions.assertEquals(expectedResponseBody, response.content().toString(StandardCharsets.UTF_8));
// Assertions.assertEquals(expectedResponseBody.length(), response.headers().getInt(HttpHeaderNames.CONTENT_LENGTH));
// }
// },
TFB_LIKE_MAP {
@Override
FullHttpRequest request() {
FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/tfblike/map");
request.headers().add(HttpHeaderNames.ACCEPT, "application/json");
return request;
}

@Override
void verifyResponse(FullHttpResponse response) {
Assertions.assertEquals(HttpResponseStatus.OK, response.status());
Assertions.assertEquals("application/json", response.headers().get(HttpHeaderNames.CONTENT_TYPE));
String expectedResponseBody = """
{"message":"Hello, World!"}""";
Assertions.assertEquals(expectedResponseBody, response.content().toString(StandardCharsets.UTF_8));
Assertions.assertEquals(expectedResponseBody.length(), response.headers().getInt(HttpHeaderNames.CONTENT_LENGTH));
}
},
MISSING_QUERY_PARAMETER {
@Override
FullHttpRequest request() {
Expand All @@ -152,8 +303,7 @@ void verifyResponse(FullHttpResponse response) {
Assertions.assertEquals(expectedResponseBody, response.content().toString(StandardCharsets.UTF_8));
Assertions.assertEquals(expectedResponseBody.length(), response.headers().getInt(HttpHeaderNames.CONTENT_LENGTH));
}
}
;
};

abstract FullHttpRequest request();

Expand All @@ -164,11 +314,64 @@ void verifyResponse(FullHttpResponse response) {
@Requires(property = "spec.name", value = "ControllersBenchmark")
static class TfbLikeController {

private static final byte[] TEXT = "Hello, World!".getBytes(StandardCharsets.UTF_8);
public static final String STRING = "Hello, World!";
private static final byte[] BYTES = STRING.getBytes(StandardCharsets.UTF_8);
public static final List<@NotNull SomeBean1> BEANS1 = List.of(
new SomeBean1(1, "A"),
new SomeBean1(2, "B"),
new SomeBean1(3, "C")
);
public static final List<@NotNull SomeBean2> BEANS2 = List.of(
new SomeBean2(1, 123),
new SomeBean2(2, 456),
new SomeBean2(3, 789)
);

@Get(value = "/bytes", produces = MediaType.TEXT_PLAIN)
public byte[] bytes() {
return BYTES;
}
@Get(value = "/string", produces = MediaType.TEXT_PLAIN)
public String string() {
return STRING;
}

@Get("/beans1")
public List<SomeBean1> beans1() {
return BEANS1;
}

@Get(value = "/", produces = MediaType.TEXT_PLAIN)
public byte[] getPlainText() {
return TEXT;
@Get("/beans2")
public List<SomeBean2> beans2() {
return BEANS2;
}

@Get("/async/beans1")
public CompletionStage<List<SomeBean1>> asyncBeans1() {
return CompletableFuture.completedFuture(BEANS1);
}

@Get("/async/beans2")
public CompletionStage<List<SomeBean2>> asyncBeans2() {
return CompletableFuture.completedFuture(BEANS2);
}

@SingleResult
@Get("/reactive/beans1")
public Publisher<List<SomeBean1>> publisherBeans1() {
return Mono.just(BEANS1);
}

@Get("/reactive/beans2")
public Mono<List<SomeBean2>> publisherBeans2() {
return Mono.just(BEANS2);
}

@Get("/map")
public Map<String, String> getJson() {
final Map<String, String> map = new HashMap<>();
map.put("message", STRING);
return map;
}
}

Expand All @@ -188,4 +391,36 @@ String echoMissingParameter(String text,
return text;
}
}

public record SomeBean1(int id, String message) {
}

public static class SomeBean2 {
private int id;
private int randomNumber;

public SomeBean2() {
}

public SomeBean2(int id, int randomNumber) {
this.id = id;
this.randomNumber = randomNumber;
}

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public int getRandomNumber() {
return randomNumber;
}

public void setRandomNumber(int randomNumber) {
this.randomNumber = randomNumber;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,34 +46,45 @@ private ReactivePropagation() {
* @return propagation aware publisher
*/
public static <T> Publisher<T> propagate(PropagatedContext propagatedContext, Publisher<T> actual) {
if (actual instanceof CorePublisher) {
return new CorePublisher<>() {
@Override
public void subscribe(@NonNull CoreSubscriber<? super T> subscriber) {
CorePublisher<T> actualCorePublisher = (CorePublisher<T>) actual;
try (PropagatedContext.Scope ignore = propagatedContext.propagate()) {
actualCorePublisher.subscribe(propagate(propagatedContext, subscriber));
}
}

@Override
public void subscribe(Subscriber<? super T> subscriber) {
if (subscriber instanceof CoreSubscriber<? super T> coreSubscriber) {
subscribe(coreSubscriber);
return;
}
try (PropagatedContext.Scope ignore = propagatedContext.propagate()) {
actual.subscribe(propagate(propagatedContext, subscriber));
}
}
};
if (actual instanceof CorePublisher<T> corePublisher) {
return propagate(propagatedContext, corePublisher);
}
return subscriber -> {
try (PropagatedContext.Scope ignore = propagatedContext.propagate()) {
actual.subscribe(propagate(propagatedContext, subscriber));
}
};
}
/**
* Creates propagation context aware {@link Publisher}.
*
* @param propagatedContext The context
* @param actual The publisher
* @param <T> The publisher element type
* @return propagation aware publisher
* @since 4.8
*/
public static <T> Publisher<T> propagate(PropagatedContext propagatedContext, CorePublisher<T> actual) {
return new CorePublisher<>() {
@Override
public void subscribe(@NonNull CoreSubscriber<? super T> subscriber) {
try (PropagatedContext.Scope ignore = propagatedContext.propagate()) {
actual.subscribe(propagate(propagatedContext, subscriber));
}
}

@Override
public void subscribe(Subscriber<? super T> subscriber) {
if (subscriber instanceof CoreSubscriber<? super T> coreSubscriber) {
subscribe(coreSubscriber);
return;
}
try (PropagatedContext.Scope ignore = propagatedContext.propagate()) {
actual.subscribe(propagate(propagatedContext, subscriber));
}
}
};
}

/**
* Creates propagation context aware {@link Subscriber}.
Expand Down
Loading

0 comments on commit 7cf4f31

Please sign in to comment.