Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reactive routes - handle RC.getBodyAsJson() returning null correctly #10871

Merged
merged 2 commits into from
Jul 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -172,10 +172,6 @@ private Methods() {
// Avoid direct instantiation
}

static void fail(BytecodeCreator creator, ResultHandle rc, ResultHandle exception) {
creator.invokeInterfaceMethod(FAIL, rc, exception);
}

static void returnAndClose(BytecodeCreator creator) {
creator.returnValue(null);
creator.close();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
import io.quarkus.vertx.web.runtime.RouteHandler;
import io.quarkus.vertx.web.runtime.RouteMatcher;
import io.quarkus.vertx.web.runtime.RoutingExchangeImpl;
import io.quarkus.vertx.web.runtime.UniFailureCallback;
import io.quarkus.vertx.web.runtime.VertxWebRecorder;
import io.vertx.core.Handler;
import io.vertx.core.http.HttpMethod;
Expand Down Expand Up @@ -557,11 +558,11 @@ void implementInvoke(HandlerDescriptor descriptor, BeanInfo bean, MethodInfo met
// If the provided item is not null, and it's an object, the item is mapped to JSON and written into the response

FunctionCreator successCallback = getUniOnItemCallback(descriptor, invoke, routingContext, end, response);
FunctionCreator failureCallback = getUniOnFailureCallback(invoke, routingContext);
ResultHandle failureCallback = getUniOnFailureCallback(invoke, routingContext);

ResultHandle sub = invoke.invokeInterfaceMethod(Methods.UNI_SUBSCRIBE, res);
invoke.invokeVirtualMethod(Methods.UNI_SUBSCRIBE_WITH, sub, successCallback.getInstance(),
failureCallback.getInstance());
failureCallback);
} else if (descriptor.isReturningMulti()) {

// 3 cases - regular multi vs. sse multi vs. json array multi, we need to check the type.
Expand Down Expand Up @@ -756,23 +757,10 @@ private FunctionCreator getUniOnItemCallback(HandlerDescriptor descriptor, Metho
return callback;
}

/**
* Generates the following function:
*
* <pre>
* throwable -> rc.fail(throwable);
* </pre>
*
* @param writer the bytecode writer
* @param rc the reference to the RoutingContext variable
* @return the function creator.
*/
private FunctionCreator getUniOnFailureCallback(MethodCreator writer, ResultHandle rc) {
FunctionCreator callback = writer.createFunction(Consumer.class);
BytecodeCreator creator = callback.getBytecode();
Methods.fail(creator, rc, creator.getMethodParam(0));
Methods.returnAndClose(creator);
return callback;
private ResultHandle getUniOnFailureCallback(MethodCreator writer, ResultHandle routingContext) {
// new UniFailureCallback(ctx)
return writer.newInstance(MethodDescriptor.ofConstructor(UniFailureCallback.class, RoutingContext.class),
routingContext);
}

private ResultHandle getContentToWrite(HandlerDescriptor descriptor, ResultHandle response, ResultHandle res,
Expand Down Expand Up @@ -1119,9 +1107,17 @@ public ResultHandle get(MethodInfo method, Type paramType, Set<AnnotationInstanc
public ResultHandle get(MethodInfo method, Type paramType, Set<AnnotationInstance> annotations,
ResultHandle routingContext,
MethodCreator invoke, int position) {
return invoke.invokeVirtualMethod(Methods.JSON_OBJECT_MAP_TO,
invoke.invokeInterfaceMethod(Methods.GET_BODY_AS_JSON, routingContext),
invoke.loadClass(paramType.name().toString()));
AssignableResultHandle ret = invoke.createVariable(Object.class);
ResultHandle bodyAsJson = invoke.invokeInterfaceMethod(Methods.GET_BODY_AS_JSON,
routingContext);
BranchResult bodyIfNotNull = invoke.ifNotNull(bodyAsJson);
BytecodeCreator bodyNotNull = bodyIfNotNull.trueBranch();
bodyNotNull.assign(ret, bodyNotNull.invokeVirtualMethod(Methods.JSON_OBJECT_MAP_TO,
bodyAsJson,
invoke.loadClass(paramType.name().toString())));
BytecodeCreator bodyNull = bodyIfNotNull.falseBranch();
bodyNull.assign(ret, bodyNull.loadNull());
return ret;
}
}).build());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
* Identifies a route method parameter that should be injected with a value returned from
* {@link HttpServerRequest#getHeader(String)}.
* <p>
* The parameter type must be {@link String} or {@code Optional<String>}, otherwise the build fails.
* The parameter type must be {@link String}, {@code java.util.Optional<String>} or {@code java.util.List<String>}, otherwise
* the build fails.
*
* @see HttpServerRequest#getHeader(String)
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
* Identifies a route method parameter that should be injected with a value returned from
* {@link HttpServerRequest#getParam(String)}.
* <p>
* The parameter type must be {@link String} or {@code Optional<String>}, otherwise the build fails.
* The parameter type must be {@link String}, {@code java.util.Optional<String>} or {@code java.util.List<String>}, otherwise
* the build fails.
*
* @see HttpServerRequest#getParam(String)
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,47 @@
* <li>{@code io.vertx.reactivex.core.http.HttpServerRequest}</li>
* <li>{@code io.vertx.reactivex.core.http.HttpServerResponse}</li>
* </ul>
* Furthermore, it is possible to inject the request parameters into a method parameter annotated with
* {@link io.quarkus.vertx.web.Param}:
*
* <pre>
* <code>
* class Routes {
* {@literal @Route}
* String hello({@literal @Param Optional<String>} name) {
* return "Hello " + name.orElse("world");
* }
* }
* </code>
* </pre>
*
* The request headers can be injected into a method parameter annotated with {@link io.quarkus.vertx.web.Header}:
*
* <pre>
* <code>
* class Routes {
* {@literal @Route}
* String helloFromHeader({@literal @Header("My-Header")} String header) {
* return "Hello " + header;
* }
* }
* </code>
* </pre>
*
* The request body can be injected into a method parameter annotated with {@link io.quarkus.vertx.web.Body}:
*
* <pre>
* <code>
* class Routes {
* {@literal @Route(produces = "application/json")}
* Person updatePerson({@literal @Body} Person person) {
* person.setName("Bob");
* return person;
* }
* }
* </code>
* </pre>
*
* If the annotated method returns {@code void} then it has to accept at least one argument.
* If the annotated method does not return {@code void} then the arguments are optional.
* <p>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package io.quarkus.vertx.web.runtime;

import java.util.function.Consumer;

import io.vertx.ext.web.RoutingContext;

public final class UniFailureCallback implements Consumer<Throwable> {

private final RoutingContext context;

public UniFailureCallback(RoutingContext context) {
this.context = context;
}

@Override
public void accept(Throwable t) {
context.fail(t);
}

}