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

Added multiple handlers support inside route object #652

Merged
merged 2 commits into from Jul 5, 2017
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
49 changes: 32 additions & 17 deletions vertx-web/src/main/java/io/vertx/ext/web/impl/RouteImpl.java
Expand Up @@ -49,8 +49,10 @@ public class RouteImpl implements Route {
private String path;
private int order;
private boolean enabled = true;
private Handler<RoutingContext> contextHandler;
private Handler<RoutingContext> failureHandler;
private List<Handler<RoutingContext>> contextHandlers;
private int actualHandlerIndex;
private List<Handler<RoutingContext>> failureHandlers;
private int actualFailureHandlerIndex;
private boolean added;
private Pattern pattern;
private List<String> groups;
Expand All @@ -59,6 +61,9 @@ public class RouteImpl implements Route {
RouteImpl(RouterImpl router, int order) {
this.router = router;
this.order = order;
this.contextHandlers = new ArrayList<>();
this.failureHandlers = new ArrayList<>();
resetIndexes();
}

RouteImpl(RouterImpl router, int order, HttpMethod method, String path) {
Expand Down Expand Up @@ -134,10 +139,7 @@ public synchronized Route last() {

@Override
public synchronized Route handler(Handler<RoutingContext> contextHandler) {
if (this.contextHandler != null) {
throw new IllegalStateException("Setting handler for a route more than once!");
}
this.contextHandler = contextHandler;
this.contextHandlers.add(contextHandler);
checkAdd();
return this;
}
Expand All @@ -154,10 +156,7 @@ public synchronized Route blockingHandler(Handler<RoutingContext> contextHandler

@Override
public synchronized Route failureHandler(Handler<RoutingContext> exceptionHandler) {
if (this.failureHandler != null) {
throw new IllegalStateException("Setting failureHandler for a route more than once!");
}
this.failureHandler = exceptionHandler;
this.failureHandlers.add(exceptionHandler);
checkAdd();
return this;
}
Expand Down Expand Up @@ -196,8 +195,8 @@ public String toString() {
StringBuilder sb = new StringBuilder("Route[ ");
sb.append("path:").append(path);
sb.append(" pattern:").append(pattern);
sb.append(" handler:").append(contextHandler);
sb.append(" failureHandler:").append(failureHandler);
sb.append(" handlers:").append(contextHandlers);
sb.append(" failureHandlers:").append(failureHandlers);
sb.append(" order:").append(order);
sb.append(" methods:[");
int cnt = 0;
Expand All @@ -213,20 +212,22 @@ public String toString() {
}

synchronized void handleContext(RoutingContext context) {
if (contextHandler != null) {
contextHandler.handle(context);
if (this.hasNextContextHandler()) {
actualHandlerIndex++;
contextHandlers.get(actualHandlerIndex - 1).handle(context);
}
}

synchronized void handleFailure(RoutingContext context) {
if (failureHandler != null) {
failureHandler.handle(context);
if (this.hasNextFailureHandler()) {
actualFailureHandlerIndex++;
failureHandlers.get(actualFailureHandlerIndex - 1).handle(context);
}
}

synchronized boolean matches(RoutingContext context, String mountPoint, boolean failure) {

if (failure && failureHandler == null || !failure && contextHandler == null) {
if (failure && !hasNextFailureHandler() || !failure && !hasNextContextHandler()) {
return false;
}
if (!enabled) {
Expand Down Expand Up @@ -416,4 +417,18 @@ private void checkAdd() {
}
}

synchronized protected boolean hasNextContextHandler() {
if (actualHandlerIndex < contextHandlers.size()) return true;
else return false;
}

synchronized protected boolean hasNextFailureHandler() {
if (actualFailureHandlerIndex < failureHandlers.size()) return true;
else return false;
}

synchronized protected void resetIndexes() {
actualFailureHandlerIndex = 0;
actualHandlerIndex = 0;
}
}
Expand Up @@ -417,6 +417,7 @@ private Set<FileUpload> getFileUploads() {

private void doFail() {
this.iter = router.iterator();
currentRoute = null;
next();
}

Expand Down
Expand Up @@ -65,8 +65,31 @@ protected void restart() {

protected boolean iterateNext() {
boolean failed = failed();
while (iter.hasNext()) {
if (currentRoute != null) { // Handle multiple handlers inside route object
try {
if (!failed && currentRoute.hasNextContextHandler()) {
currentRoute.handleContext(this);
return true;
} else if (failed && currentRoute.hasNextFailureHandler()) {
currentRoute.handleFailure(this);
return true;
}
} catch (Throwable t) {
if (log.isTraceEnabled()) log.trace("Throwable thrown from handler", t);
if (!failed) {
if (log.isTraceEnabled()) log.trace("Failing the routing");
fail(t);
} else {
// Failure in handling failure!
if (log.isTraceEnabled()) log.trace("Failure in handling failure");
unhandledFailure(-1, t, currentRoute.router());
}
return true;
}
}
while (iter.hasNext()) { // Search for more handlers
RouteImpl route = iter.next();
route.resetIndexes();
if (route.matches(this, mountPoint(), failed)) {
if (log.isTraceEnabled()) log.trace("Route matches: " + route);
try {
Expand Down
112 changes: 100 additions & 12 deletions vertx-web/src/test/java/io/vertx/ext/web/RouterTest.java
Expand Up @@ -19,6 +19,7 @@
import io.vertx.core.Handler;
import io.vertx.core.MultiMap;
import io.vertx.core.http.HttpMethod;
import io.vertx.core.http.HttpServerResponse;
import org.junit.Test;

import java.util.List;
Expand Down Expand Up @@ -2002,21 +2003,108 @@ public void testGetWithPlusPath2() throws Exception {

@Test
public void testMultipleSetHandler() throws Exception {
try {
router.route().handler(context -> {}).handler(context -> {});
fail();
} catch (IllegalStateException e) {
// OK
}
router.get("/path").handler(routingContext -> {
routingContext.put("response", "handler1");
routingContext.next();
}).handler(routingContext -> {
routingContext.put("response", routingContext.get("response") + "handler2");
routingContext.next();
}).handler(routingContext -> {
HttpServerResponse response = routingContext.response();
response.setChunked(true);
response.end(routingContext.get("response") + "handler3");
});
testRequest(HttpMethod.GET, "/path", 200, "OK", "handler1handler2handler3");
}

@Test
public void testMultipleSetFailureHandler() throws Exception {
try {
router.route().failureHandler(context -> {}).failureHandler(context -> {});
fail();
} catch (IllegalStateException e) {
// OK
}
router.get("/path").handler(routingContext -> {
routingContext.fail(500);
}).failureHandler(routingContext -> {
routingContext.put("response", "handler1");
routingContext.next();
}).failureHandler(routingContext -> {
routingContext.put("response", routingContext.get("response") + "handler2");
routingContext.next();
}).failureHandler(routingContext -> {
HttpServerResponse response = routingContext.response();
response.setChunked(true);
response.setStatusMessage("ERROR");
response.setStatusCode(500);
response.end(routingContext.get("response") + "handler3");
});
testRequest(HttpMethod.GET, "/path", 500, "ERROR", "handler1handler2handler3");
}

@Test
public void testMultipleSetFailureHandlerCorrectOrder() throws Exception {
router.route().failureHandler(routingContext -> {
routingContext.put("response", "handler1");
routingContext.next();
});

router.get("/path").handler(routingContext -> {
routingContext.fail(500);
}).failureHandler(routingContext -> {
routingContext.put("response", routingContext.get("response") + "handler2");
routingContext.next();
}).failureHandler(routingContext -> {
HttpServerResponse response = routingContext.response();
response.setChunked(true);
response.setStatusMessage("ERROR");
response.setStatusCode(500);
response.end(routingContext.get("response") + "handler3");
});
testRequest(HttpMethod.GET, "/path", 500, "ERROR", "handler1handler2handler3");
}

@Test
public void testMultipleHandlersMixed() throws Exception {
router.route().failureHandler(routingContext -> {
routingContext.put("response", "fhandler1");
routingContext.next();
});

router.get("/:param").handler(routingContext -> {
if (routingContext.pathParam("param").equals("fail")) routingContext.fail(500);
routingContext.put("response", "handler1");
routingContext.next();
}).handler(routingContext -> {
routingContext.put("response", routingContext.get("response") + "handler2");
routingContext.next();
}).handler(routingContext -> {
HttpServerResponse response = routingContext.response();
response.setChunked(true);
response.end(routingContext.get("response") + "handler3");
}).failureHandler(routingContext -> {
routingContext.put("response", routingContext.get("response") + "fhandler2");
routingContext.next();
}).failureHandler(routingContext -> {
HttpServerResponse response = routingContext.response();
response.setChunked(true);
response.setStatusMessage("ERROR");
response.setStatusCode(500);
response.end(routingContext.get("response") + "fhandler3");
});
testRequest(HttpMethod.GET, "/path", 200, "OK", "handler1handler2handler3");
testRequest(HttpMethod.GET, "/fail", 500, "ERROR", "fhandler1fhandler2fhandler3");
}

@Test
public void testMultipleSetHandlerMultipleRouteObject() throws Exception {
router.get("/path").handler(routingContext -> {
routingContext.put("response", "handler1");
routingContext.next();
});
router.get("/path").handler(routingContext -> {
routingContext.put("response", routingContext.get("response") + "handler2");
routingContext.next();
}).handler(routingContext -> {
HttpServerResponse response = routingContext.response();
response.setChunked(true);
response.end(routingContext.get("response") + "handler3");
});
testRequest(HttpMethod.GET, "/path", 200, "OK", "handler1handler2handler3");
}
}