Skip to content

Commit

Permalink
kotlin: mount of coroutine routes doesn't work
Browse files Browse the repository at this point in the history
 - fix #3228
  • Loading branch information
Edgar Espina committed Feb 26, 2024
1 parent 285fa53 commit 92ee4e7
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 24 deletions.
57 changes: 33 additions & 24 deletions jooby/src/main/java/io/jooby/internal/RouterImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
Expand Down Expand Up @@ -308,8 +309,7 @@ public RouteSet mount(@NonNull Predicate<Context> predicate, @NonNull Runnable b
@NonNull @Override
public Router mount(@NonNull Predicate<Context> predicate, @NonNull Router subrouter) {
/** Override services: */
overrideServices(subrouter);

overrideAll(this, subrouter);
/** Routes: */
mount(
predicate,
Expand All @@ -325,7 +325,7 @@ public Router mount(@NonNull Predicate<Context> predicate, @NonNull Router subro
@NonNull @Override
public Router mount(@NonNull String path, @NonNull Router router) {
/** Override services: */
overrideServices(router);
overrideAll(this, router);
/** Merge error handler: */
mergeErrorHandler(router);
/** Routes: */
Expand Down Expand Up @@ -857,7 +857,7 @@ public String toString() {
buff.append(String.format("\n %-" + size + "s", r.getMethod()))
.append(r.getPattern()));
}
return buff.length() > 0 ? buff.substring(1) : "";
return !buff.isEmpty() ? buff.substring(1) : "";
}

private Router newStack(RouteTree tree, String pattern, Runnable action, Route.Filter... filter) {
Expand All @@ -870,7 +870,7 @@ private Stack push(RouteTree tree) {

private Stack push(RouteTree tree, String pattern) {
Stack stack = new Stack(tree, Router.leadingSlash(pattern));
if (this.stack.size() > 0) {
if (!this.stack.isEmpty()) {
Stack parent = this.stack.getLast();
stack.executor = parent.executor;
}
Expand All @@ -880,34 +880,38 @@ private Stack push(RouteTree tree, String pattern) {
private Router newStack(@NonNull Stack stack, @NonNull Runnable action, Route.Filter... filter) {
Stream.of(filter).forEach(stack::then);
this.stack.addLast(stack);
if (action != null) {
action.run();
}
action.run();
this.stack.removeLast().clear();
return this;
}

private void copy(Route src, Route it) {
Route.Filter decorator =
var filter =
Optional.ofNullable(it.getFilter())
.map(filter -> Optional.ofNullable(src.getFilter()).map(filter::then).orElse(filter))
.map(e -> Optional.ofNullable(src.getFilter()).map(e::then).orElse(e))
.orElseGet(src::getFilter);

Route.After after =
var after =
Optional.ofNullable(it.getAfter())
.map(filter -> Optional.ofNullable(src.getAfter()).map(filter::then).orElse(filter))
.map(e -> Optional.ofNullable(src.getAfter()).map(e::then).orElse(e))
.orElseGet(src::getAfter);

it.setFilter(decorator);
it.setPathKeys(src.getPathKeys());
it.setFilter(filter);
it.setAfter(after);

it.setConsumes(src.getConsumes());
it.setProduces(src.getProduces());
it.setHandle(src.getHandle());
it.setEncoder(src.getEncoder());
it.setReturnType(src.getReturnType());
it.setHandle(src.getHandle());
it.setProduces(src.getProduces());
it.setConsumes(src.getConsumes());
it.setAttributes(src.getAttributes());
it.setExecutorKey(src.getExecutorKey());
it.setHandle(src.getHandle());
it.setTags(src.getTags());
it.setDescription(src.getDescription());
it.setDecoders(src.getDecoders());
it.setMvcMethod(it.getMvcMethod());
it.setNonBlocking(src.isNonBlocking());
it.setSummary(src.getSummary());
}

private void putPredicate(@NonNull Predicate<Context> predicate, Chi tree) {
Expand Down Expand Up @@ -968,17 +972,22 @@ private void copyRoutes(@NonNull String path, @NonNull Router router) {
}
}

private void overrideServices(Router router) {
private static void override(
RouterImpl src, Router router, BiConsumer<RouterImpl, RouterImpl> consumer) {
if (router instanceof Jooby) {
Jooby app = (Jooby) router;
overrideServices(app.getRouter());
} else if (router instanceof RouterImpl) {
RouterImpl that = (RouterImpl) router;
// Inherited the services from router owner
that.services = this.services;
override(src, app.getRouter(), consumer);
} else if (router instanceof RouterImpl that) {
consumer.accept((RouterImpl) src, that);
}
}

private void overrideAll(RouterImpl src, Router router) {
override(src, router, (self, that) -> that.services = self.services);
override(src, router, (self, that) -> that.worker = self.worker);
override(src, router, (self, that) -> that.attributes.putAll(self.attributes));
}

private void mergeErrorHandler(Router router) {
if (router instanceof Jooby) {
Jooby app = (Jooby) router;
Expand Down
71 changes: 71 additions & 0 deletions tests/src/test/kotlin/i3228/Issue3228.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Jooby https://jooby.io
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
* Copyright 2014 Edgar Espina
*/
package i3228

import io.jooby.junit.ServerTest
import io.jooby.junit.ServerTestRunner
import io.jooby.kt.Kooby
import java.util.concurrent.Executor
import org.junit.jupiter.api.Assertions

class RouterWithoutWorker :
Kooby({
coroutine {
get("/i3228/without-worker") {
"nonBlocking: " +
ctx.route.isNonBlocking +
", coroutine: " +
ctx.route.attributes["coroutine"]
}
}
})

class RouterWithoutWorkerNoCoroutine :
Kooby({
get("/i3228/without-worker-no-coroutine") {
"nonBlocking: " +
ctx.route.isNonBlocking +
", coroutine: " +
ctx.route.attributes["coroutine"]
}
})

class RouterWithWorker(worker: Executor) :
Kooby({
this.worker = worker
coroutine {
get("/i3228/with-worker") {
"nonBlocking: " +
ctx.route.isNonBlocking +
", coroutine: " +
ctx.route.attributes["coroutine"]
}
}
})

class Issue3228 {
@ServerTest
fun shouldCopyCoroutineState(runner: ServerTestRunner) =
runner
.use {
Kooby { ->
mount(RouterWithWorker(this.worker))
mount(RouterWithoutWorker())
coroutine { mount(RouterWithoutWorkerNoCoroutine()) }
}
}
.ready { client ->
client.get("/i3228/without-worker") { rsp ->
Assertions.assertEquals("nonBlocking: true, coroutine: true", rsp.body!!.string())
}
client.get("/i3228/without-worker-no-coroutine") { rsp ->
Assertions.assertEquals("nonBlocking: true, coroutine: true", rsp.body!!.string())
}
client.get("/i3228/with-worker") { rsp ->
Assertions.assertEquals("nonBlocking: true, coroutine: true", rsp.body!!.string())
}
}
}

0 comments on commit 92ee4e7

Please sign in to comment.