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

Allow suspend functions as to be uses as custom filters #20529

Merged
merged 1 commit into from
Oct 5, 2021
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
@@ -0,0 +1,23 @@
package io.quarkus.resteasy.reactive.common.deployment;

import static io.quarkus.resteasy.reactive.common.deployment.QuarkusResteasyReactiveDotNames.CONTINUATION;

import java.util.List;

import org.jboss.jandex.MethodInfo;
import org.jboss.jandex.Type;

public class KotlinUtils {

private KotlinUtils() {
}

public static boolean isSuspendMethod(MethodInfo methodInfo) {
List<Type> parameters = methodInfo.parameters();
if (parameters.isEmpty()) {
return false;
}

return CONTINUATION.equals(parameters.get(parameters.size() - 1).name());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ public class QuarkusResteasyReactiveDotNames {
public static final DotName HTTP_SERVER_RESPONSE = DotName.createSimple(HttpServerResponse.class.getName());
public static final DotName JSON_IGNORE = DotName.createSimple("com.fasterxml.jackson.annotation.JsonIgnore");
public static final DotName JSONB_TRANSIENT = DotName.createSimple("javax.json.bind.annotation.JsonbTransient");

public static final DotName CONTINUATION = DotName.createSimple("kotlin.coroutines.Continuation");
public static final DotName KOTLIN_UNIT = DotName.createSimple("kotlin.Unit");

public static final IgnoreTypeForReflectionPredicate IGNORE_TYPE_FOR_REFLECTION_PREDICATE = new IgnoreTypeForReflectionPredicate();
public static final IgnoreFieldForReflectionPredicate IGNORE_FIELD_FOR_REFLECTION_PREDICATE = new IgnoreFieldForReflectionPredicate();
public static final IgnoreMethodForReflectionPredicate IGNORE_METHOD_FOR_REFLECTION_PREDICATE = new IgnoreMethodForReflectionPredicate();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package org.jboss.resteasy.reactive.server.runtime.kotlin

import io.smallrye.mutiny.Uni
import io.smallrye.mutiny.coroutines.asUni
import kotlinx.coroutines.async
import org.jboss.resteasy.reactive.server.core.ResteasyReactiveRequestContext
import org.jboss.resteasy.reactive.server.spi.ResteasyReactiveContainerRequestContext
import org.jboss.resteasy.reactive.server.spi.ResteasyReactiveContainerRequestFilter

/**
* Base class used by Quarkus to generate an implementation at build-time that calls
* a {@code suspend} method annotated with {@code @ServerRequestFilter}
*/
@Suppress("unused")
abstract class AbstractSuspendedRequestFilter : ResteasyReactiveContainerRequestFilter {

abstract suspend fun doFilter(containerRequestContext: ResteasyReactiveContainerRequestContext): Any

abstract fun handleResult(containerRequestContext: ResteasyReactiveContainerRequestContext, uniResult: Uni<*>)


private val originalTCCL: ClassLoader = Thread.currentThread().contextClassLoader

override fun filter(containerRequestContext: ResteasyReactiveContainerRequestContext) {
val (dispatcher,coroutineScope) = prepareExecution(containerRequestContext.serverRequestContext as ResteasyReactiveRequestContext)

val uni = coroutineScope.async(context = dispatcher) {
// ensure the proper CL is not lost in dev-mode
Thread.currentThread().contextClassLoader = originalTCCL

// the implementation gets the proper values from the context and invokes the user supplied method
doFilter(containerRequestContext)
}.asUni()

// the implementation should call the appropriate FilterUtil method
handleResult(containerRequestContext, uni)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package org.jboss.resteasy.reactive.server.runtime.kotlin

import kotlinx.coroutines.launch
import org.jboss.resteasy.reactive.server.core.ResteasyReactiveRequestContext
import org.jboss.resteasy.reactive.server.spi.ResteasyReactiveContainerRequestContext
import org.jboss.resteasy.reactive.server.spi.ResteasyReactiveContainerResponseFilter
import javax.ws.rs.container.ContainerResponseContext

/**
* Base class used by Quarkus to generate an implementation at build-time that calls
* a {@code suspend} method annotated with {@code @ServerResponseFilter}
*/
@Suppress("unused")
abstract class AbstractSuspendedResponseFilter : ResteasyReactiveContainerResponseFilter {

abstract suspend fun doFilter(requestContext: ResteasyReactiveContainerRequestContext, responseContext: ContainerResponseContext): Any

private val originalTCCL: ClassLoader = Thread.currentThread().contextClassLoader

override fun filter(requestContext: ResteasyReactiveContainerRequestContext, responseContext: ContainerResponseContext) {
val (dispatcher,coroutineScope) = prepareExecution(requestContext.serverRequestContext as ResteasyReactiveRequestContext)


requestContext.suspend()
coroutineScope.launch(context = dispatcher) {
// ensure the proper CL is not lost in dev-mode
Thread.currentThread().contextClassLoader = originalTCCL
try {
doFilter(requestContext, responseContext)
} catch (t: Throwable) {
(requestContext.serverRequestContext as ResteasyReactiveRequestContext).handleException(t, true)
}
requestContext.resume()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package org.jboss.resteasy.reactive.server.runtime.kotlin

import io.vertx.core.Vertx
import kotlinx.coroutines.CoroutineDispatcher
import org.jboss.resteasy.reactive.server.core.ResteasyReactiveRequestContext
import javax.enterprise.inject.spi.CDI

fun prepareExecution(requestContext: ResteasyReactiveRequestContext): Pair<CoroutineDispatcher, ApplicationCoroutineScope> {
val requestScope = requestContext.captureCDIRequestScope()
val dispatcher: CoroutineDispatcher = Vertx.currentContext()?.let {VertxDispatcher(it,requestScope)}
?: throw IllegalStateException("No Vertx context found")

val coroutineScope = CDI.current().select(ApplicationCoroutineScope::class.java)
requestContext.suspend()

return Pair(dispatcher, coroutineScope.get())
}
Loading