Skip to content

Commit

Permalink
Properly determine return type in RESTEasy Reactive methods
Browse files Browse the repository at this point in the history
This is done in order to support complex return types like
`Uni<RestResponse<AsyncFile>>`

Fixes: #23489
  • Loading branch information
geoand committed Feb 8, 2022
1 parent b6b6ba7 commit c0a19c3
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import org.jboss.resteasy.reactive.FilePart;
import org.jboss.resteasy.reactive.PathPart;
import org.jboss.resteasy.reactive.RestResponse;

import io.smallrye.mutiny.Uni;
import io.vertx.core.file.AsyncFile;
Expand Down Expand Up @@ -56,6 +57,19 @@ public Uni<AsyncFile> getAsyncFile(RoutingContext vertxRequest) {
});
}

@Path("rest-response-async-file")
@GET
public Uni<RestResponse<AsyncFile>> getRestResponseAsyncFile(RoutingContext vertxRequest) {
return Uni.createFrom().emitter(emitter -> {
vertxRequest.vertx().fileSystem().open(FILE, new OpenOptions(), result -> {
if (result.succeeded())
emitter.complete(RestResponse.ResponseBuilder.ok(result.result()).header("foo", "bar").build());
else
emitter.fail(result.cause());
});
});
}

@Path("mutiny-async-file")
@GET
public Uni<io.vertx.mutiny.core.file.AsyncFile> getMutinyAsyncFile(RoutingContext vertxRequest) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@ public void testFiles() throws Exception {
.header(HttpHeaders.CONTENT_LENGTH, Matchers.nullValue())
.statusCode(200)
.body(Matchers.equalTo(content));
RestAssured.get("/providers/file/rest-response-async-file")
.then()
.header("foo", "bar")
.statusCode(200)
.body(Matchers.equalTo(content));
RestAssured.get("/providers/file/mutiny-async-file")
.then()
.header(HttpHeaders.CONTENT_LENGTH, Matchers.nullValue())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -358,8 +358,8 @@ public RuntimeResource buildResourceMethod(ResourceClass clazz,
}

Type returnType = TypeSignatureParser.parse(method.getReturnType());
Type nonAsyncReturnType = getNonAsyncReturnType(returnType);
Class<?> rawNonAsyncReturnType = getRawType(nonAsyncReturnType);
Type effectiveReturnType = getEffectiveReturnType(returnType);
Class<?> rawEffectiveReturnType = getRawType(effectiveReturnType);

ServerMediaType serverMediaType = null;
if (method.getProduces() != null && method.getProduces().length > 0) {
Expand All @@ -371,7 +371,7 @@ public RuntimeResource buildResourceMethod(ResourceClass clazz,
if (method.getHttpMethod() == null) {
//this is a resource locator method
handlers.add(resourceLocatorHandler);
} else if (!Response.class.isAssignableFrom(rawNonAsyncReturnType)) {
} else if (!Response.class.isAssignableFrom(rawEffectiveReturnType)) {
//try and statically determine the media type and response writer
//we can't do this for all cases, but we can do it for the most common ones
//in practice this should work for the majority of endpoints
Expand All @@ -383,9 +383,9 @@ public RuntimeResource buildResourceMethod(ResourceClass clazz,
if (mediaType.isWildcardType() || mediaType.isWildcardSubtype()) {
handlers.add(new VariableProducesHandler(serverMediaType, serialisers));
score.add(ScoreSystem.Category.Writer, ScoreSystem.Diagnostic.WriterRunTime);
} else if (rawNonAsyncReturnType != Void.class
&& rawNonAsyncReturnType != void.class) {
List<MessageBodyWriter<?>> buildTimeWriters = serialisers.findBuildTimeWriters(rawNonAsyncReturnType,
} else if (rawEffectiveReturnType != Void.class
&& rawEffectiveReturnType != void.class) {
List<MessageBodyWriter<?>> buildTimeWriters = serialisers.findBuildTimeWriters(rawEffectiveReturnType,
RuntimeType.SERVER, Collections.singletonList(
MediaTypeHelper.withSuffixAsSubtype(MediaType.valueOf(method.getProduces()[0]))));
if (buildTimeWriters == null) {
Expand Down Expand Up @@ -465,7 +465,7 @@ public RuntimeResource buildResourceMethod(ResourceClass clazz,
method.getProduces() == null ? null : serverMediaType,
consumesMediaTypes, invoker,
clazz.getFactory(), handlers.toArray(EMPTY_REST_HANDLER_ARRAY), method.getName(), parameterDeclaredTypes,
nonAsyncReturnType, method.isBlocking(), resourceClass,
effectiveReturnType, method.isBlocking(), resourceClass,
lazyMethod,
pathParameterIndexes, info.isDevelopmentMode() ? score : null, streamElementType,
clazz.resourceExceptionMapper());
Expand Down Expand Up @@ -626,23 +626,22 @@ private Class<?> getRawType(Type type) {
throw new UnsupportedOperationException("Endpoint return type not supported yet: " + type);
}

private Type getNonAsyncReturnType(Type returnType) {
private Type getEffectiveReturnType(Type returnType) {
if (returnType instanceof Class)
return returnType;
if (returnType instanceof ParameterizedType) {
// NOTE: same code in EndpointIndexer.getNonAsyncReturnType
ParameterizedType type = (ParameterizedType) returnType;
if (type.getRawType() == CompletionStage.class) {
return type.getActualTypeArguments()[0];
return getEffectiveReturnType(type.getActualTypeArguments()[0]);
}
if (type.getRawType() == Uni.class) {
return type.getActualTypeArguments()[0];
return getEffectiveReturnType(type.getActualTypeArguments()[0]);
}
if (type.getRawType() == Multi.class) {
return type.getActualTypeArguments()[0];
return getEffectiveReturnType(type.getActualTypeArguments()[0]);
}
if (type.getRawType() == RestResponse.class) {
return type.getActualTypeArguments()[0];
return getEffectiveReturnType(type.getActualTypeArguments()[0]);
}
return returnType;
}
Expand Down

0 comments on commit c0a19c3

Please sign in to comment.