Skip to content

Commit

Permalink
Merge pull request #32815 from geoand/#31587
Browse files Browse the repository at this point in the history
Properly pass annotation to Writers when streaming data
  • Loading branch information
geoand authored Apr 21, 2023
2 parents 6530f8d + 337c3d7 commit 7ee7e40
Show file tree
Hide file tree
Showing 7 changed files with 118 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
import jakarta.ws.rs.sse.OutboundSseEvent;
import jakarta.ws.rs.sse.SseEvent;

import org.jboss.resteasy.reactive.common.core.Serialisers;
import org.jboss.resteasy.reactive.common.util.CommonSseUtil;
import org.jboss.resteasy.reactive.common.util.QuarkusMultivaluedHashMap;
import org.jboss.resteasy.reactive.server.handlers.PublisherResponseHandler;
Expand Down Expand Up @@ -135,10 +134,9 @@ private static String serialiseDataToString(ResteasyReactiveRequestContext conte
ByteArrayOutputStream baos = new ByteArrayOutputStream();
boolean wrote = false;
for (MessageBodyWriter<Object> writer : writers) {
// Spec(API) says we should use class/type/mediaType but doesn't talk about annotations
if (writer.isWriteable(entityClass, entityType, Serialisers.NO_ANNOTATION, mediaType)) {
if (writer.isWriteable(entityClass, entityType, context.getAllAnnotations(), mediaType)) {
// FIXME: spec doesn't really say what headers we should use here
writer.writeTo(entity, entityClass, entityType, Serialisers.NO_ANNOTATION, mediaType,
writer.writeTo(entity, entityClass, entityType, context.getAllAnnotations(), mediaType,
new QuarkusMultivaluedHashMap<>(), baos);
wrote = true;
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.ext.MessageBodyWriter;

import org.jboss.resteasy.reactive.common.core.Serialisers;
import org.jboss.resteasy.reactive.common.util.QuarkusMultivaluedHashMap;
import org.jboss.resteasy.reactive.server.StreamingOutputStream;
import org.jboss.resteasy.reactive.server.handlers.PublisherResponseHandler;
Expand Down Expand Up @@ -63,10 +62,9 @@ private static byte[] serialiseEntity(ResteasyReactiveRequestContext context, Ob
StreamingOutputStream baos = new StreamingOutputStream();
boolean wrote = false;
for (MessageBodyWriter<Object> writer : writers) {
// Spec(API) says we should use class/type/mediaType but doesn't talk about annotations
if (writer.isWriteable(entityClass, entityType, Serialisers.NO_ANNOTATION, mediaType)) {
if (writer.isWriteable(entityClass, entityType, context.getAllAnnotations(), mediaType)) {
// FIXME: spec doesn't really say what headers we should use here
writer.writeTo(entity, entityClass, entityType, Serialisers.NO_ANNOTATION, mediaType,
writer.writeTo(entity, entityClass, entityType, context.getAllAnnotations(), mediaType,
new QuarkusMultivaluedHashMap<>(), baos);
wrote = true;
break;
Expand Down
17 changes: 17 additions & 0 deletions integration-tests/hibernate-orm-envers/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-h2</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jaxrs-client-reactive</artifactId>
</dependency>

<!-- test dependencies -->
<dependency>
Expand Down Expand Up @@ -99,6 +103,19 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jaxrs-client-reactive-deployment</artifactId>
<version>${project.version}</version>
<type>pom</type>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package io.quarkus.it.envers;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomOutput {

String value();
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,15 @@ public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotat
@Override
public void writeTo(Message event, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType,
MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException {
entityStream.write("{\"data\": \"out\"}".getBytes(StandardCharsets.UTF_8));
String data = "out";
if (annotations != null) {
for (Annotation annotation : annotations) {
if (annotation.annotationType().equals(CustomOutput.class)) {
data = ((CustomOutput) annotation).value();
break;
}
}
}
entityStream.write(String.format("{\"data\": \"%s\"}", data).getBytes(StandardCharsets.UTF_8));
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
package io.quarkus.it.envers;

import java.util.List;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;

import org.jboss.resteasy.reactive.RestStreamElementType;

import io.smallrye.mutiny.Multi;

@Path("output")
public class OutputResource {

Expand All @@ -13,4 +19,19 @@ public class OutputResource {
public Message out() {
return new Message("test");
}

@GET
@RestStreamElementType(MediaType.APPLICATION_JSON)
@CustomOutput("dummy")
@Path("annotation")
public Multi<Message> sseOut() {
return Multi.createFrom().iterable(List.of(new Message("test"), new Message("test")));
}

@GET
@RestStreamElementType(MediaType.APPLICATION_JSON)
@Path("no-annotation")
public Multi<Message> sseOut2() {
return Multi.createFrom().iterable(List.of(new Message("test"), new Message("test")));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,73 @@
import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.equalTo;

import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import jakarta.ws.rs.client.Client;
import jakarta.ws.rs.client.ClientBuilder;
import jakarta.ws.rs.client.WebTarget;
import jakarta.ws.rs.sse.SseEventSource;

import org.eclipse.microprofile.config.ConfigProvider;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import io.quarkus.test.common.http.TestHTTPEndpoint;
import io.quarkus.test.common.http.TestHTTPResource;
import io.quarkus.test.junit.QuarkusTest;
import io.restassured.http.ContentType;

@QuarkusTest
class OutputResourceTest {

private static final String RESOURCE_PATH = "/jpa-envers-test/output";
@TestHTTPEndpoint(OutputResource.class)
@TestHTTPResource
URL url;

@Test
void test() {
given().accept(ContentType.JSON)
.when()
.get("/jpa-envers-test/output")
.get(RESOURCE_PATH)
.then()
.statusCode(200)
.body("data", equalTo("out"));
}

@Test
public void testSseWithAnnotation() throws InterruptedException, URISyntaxException, MalformedURLException {
doTestSee("annotation", "dummy");
}

@Test
public void testSseWithoutAnnotation() throws InterruptedException, URISyntaxException, MalformedURLException {
doTestSee("no-annotation", "out");
}

private void doTestSee(String path, String expectedDataValue)
throws URISyntaxException, InterruptedException, MalformedURLException {
Client client = ClientBuilder.newBuilder().build();
WebTarget target = client.target(new URL(ConfigProvider.getConfig().getValue("test.url", String.class)).toURI())
.path(RESOURCE_PATH).path(path);
try (SseEventSource sse = SseEventSource.target(target).build()) {
CountDownLatch latch = new CountDownLatch(1);
List<Throwable> errors = new CopyOnWriteArrayList<>();
List<String> results = new CopyOnWriteArrayList<>();
sse.register(event -> results.add(event.readData()), errors::add, latch::countDown);
sse.open();
Assertions.assertTrue(latch.await(20, TimeUnit.SECONDS));

String json = String.format("{\"data\": \"%s\"}", expectedDataValue);
Assertions.assertEquals(Arrays.asList(json, json), results);
Assertions.assertEquals(0, errors.size());
}
}
}

0 comments on commit 7ee7e40

Please sign in to comment.