Skip to content

Commit

Permalink
Add routing handler
Browse files Browse the repository at this point in the history
  • Loading branch information
stuartwdouglas committed Apr 14, 2014
1 parent 07e915e commit 04385f2
Show file tree
Hide file tree
Showing 8 changed files with 331 additions and 16 deletions.
18 changes: 18 additions & 0 deletions core/src/main/java/io/undertow/Handlers.java
Expand Up @@ -6,6 +6,7 @@
import io.undertow.predicate.PredicatesHandler;
import io.undertow.server.HttpHandler;
import io.undertow.server.JvmRouteHandler;
import io.undertow.server.RoutingHandler;
import io.undertow.server.handlers.AccessControlListHandler;
import io.undertow.server.handlers.DateHandler;
import io.undertow.server.handlers.GracefulShutdownHandler;
Expand Down Expand Up @@ -70,6 +71,23 @@ public static PathTemplateHandler pathTemplate() {
return new PathTemplateHandler();
}

/**
*
* @param rewriteQueryParams If the query params should be rewritten
* @return The routing handler
*/
public static RoutingHandler routing(boolean rewriteQueryParams) {
return new RoutingHandler(rewriteQueryParams);
}

/**
*
* @return a new routing handler
*/
public static RoutingHandler routing() {
return new RoutingHandler();
}

/**
*
* @param rewriteQueryParams If the query params should be rewritten
Expand Down
22 changes: 21 additions & 1 deletion core/src/main/java/io/undertow/predicate/Predicates.java
Expand Up @@ -13,7 +13,7 @@ public class Predicates {

/**
* Creates a procedure that returns true if the given ExchangeAttributes are equal.
* @param Attributes to be compared in the predictor.
* @param attributes to be compared in the predictor.
* @return A new EqualsPredicate.
*/
public static Predicate equals(final ExchangeAttribute[] attributes){
Expand Down Expand Up @@ -176,6 +176,26 @@ public static Predicate regex(final String attribute, final String pattern, fina
return new RegularExpressionPredicate(pattern, ExchangeAttributes.parser(classLoader).parse(attribute), requireFullMatch);
}

/**
* parses the predicate string, and returns the result, using the TCCL to load predicate definitions
* @param predicate The prediate string
* @return The predicate
*/
public static final Predicate parse(final String predicate) {
return PredicateParser.parse(predicate, Thread.currentThread().getContextClassLoader());
}


/**
* parses the predicate string, and returns the result
* @param predicate The prediate string
* @param classLoader The class loader to load the predicates from
* @return The predicate
*/
public static final Predicate parse(final String predicate, ClassLoader classLoader) {
return PredicateParser.parse(predicate, classLoader);
}

private Predicates() {

}
Expand Down
10 changes: 9 additions & 1 deletion core/src/main/java/io/undertow/predicate/PredicatesHandler.java
Expand Up @@ -17,7 +17,7 @@
public class PredicatesHandler implements HttpHandler {

private volatile Holder[] handlers = new Holder[0];
private final HttpHandler next;
private volatile HttpHandler next;

//non-static, so multiple handlers can co-exist
private final AttachmentKey<Integer> CURRENT_POSITION = AttachmentKey.create(Integer.class);
Expand Down Expand Up @@ -69,6 +69,14 @@ public PredicatesHandler addPredicatedHandler(final PredicatedHandler handler) {
return addPredicatedHandler(handler.getPredicate(), handler.getHandler());
}

public void setNext(HttpHandler next) {
this.next = next;
}

public HttpHandler getNext() {
return next;
}

private static final class Holder {
final Predicate predicate;
final HttpHandler handler;
Expand Down
130 changes: 130 additions & 0 deletions core/src/main/java/io/undertow/server/RoutingHandler.java
@@ -0,0 +1,130 @@
package io.undertow.server;

import io.undertow.predicate.Predicate;
import io.undertow.server.handlers.ResponseCodeHandler;
import io.undertow.util.CopyOnWriteMap;
import io.undertow.util.HttpString;
import io.undertow.util.PathTemplateMatch;
import io.undertow.util.PathTemplateMatcher;

import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;

/**
* A Handler that handles the common case of routing via path template and method name.
*
* @author Stuart Douglas
*/
public class RoutingHandler implements HttpHandler {

private final Map<HttpString, PathTemplateMatcher<RoutingMatch>> matches = new CopyOnWriteMap<HttpString, PathTemplateMatcher<RoutingMatch>>();

private volatile HttpHandler fallbackHandler = ResponseCodeHandler.HANDLE_404;

/**
* If this is true then path matches will be added to the query parameters for easy access by
* later handlers.
*/
private final boolean rewriteQueryParameters;

public RoutingHandler(boolean rewriteQueryParameters) {
this.rewriteQueryParameters = rewriteQueryParameters;
}

public RoutingHandler() {
this.rewriteQueryParameters = true;
}

@Override
public void handleRequest(HttpServerExchange exchange) throws Exception {

PathTemplateMatcher<RoutingMatch> matcher = matches.get(exchange.getRequestMethod());
if (matcher == null) {
fallbackHandler.handleRequest(exchange);
return;
}
PathTemplateMatcher.PathMatchResult<RoutingMatch> match = matcher.match(exchange.getRelativePath());
if (match == null) {
fallbackHandler.handleRequest(exchange);
return;
}
exchange.putAttachment(PathTemplateMatch.ATTACHMENT_KEY, match);
if (rewriteQueryParameters) {
for (Map.Entry<String, String> entry : match.getParameters().entrySet()) {
exchange.addQueryParam(entry.getKey(), entry.getValue());
}
}
for (HandlerHolder handler : match.getValue().predicatedHandlers) {
if (handler.predicate.resolve(exchange)) {
handler.handler.handleRequest(exchange);
return;
}
}
if (match.getValue().defaultHandler != null) {
match.getValue().defaultHandler.handleRequest(exchange);
} else {
fallbackHandler.handleRequest(exchange);
}
}

public synchronized RoutingHandler add(final String method, final String template, HttpHandler handler) {
return add(new HttpString(method), template, handler);
}

public synchronized RoutingHandler add(HttpString method, String template, HttpHandler handler) {
PathTemplateMatcher<RoutingMatch> matcher = matches.get(method);
if (matcher == null) {
matches.put(method, matcher = new PathTemplateMatcher<RoutingMatch>());
}
RoutingMatch res = matcher.get(template);
if (res == null) {
matcher.add(template, res = new RoutingMatch());
}
res.defaultHandler = handler;
return this;
}

public synchronized RoutingHandler add(final String method, final String template, Predicate predicate, HttpHandler handler) {
return add(new HttpString(method), template, predicate, handler);
}

public synchronized RoutingHandler add(HttpString method, String template, Predicate predicate, HttpHandler handler) {
PathTemplateMatcher<RoutingMatch> matcher = matches.get(method);
if (matcher == null) {
matches.put(method, matcher = new PathTemplateMatcher<RoutingMatch>());
}
RoutingMatch res = matcher.get(template);
if (res == null) {
matcher.add(template, res = new RoutingMatch());
}
res.predicatedHandlers.add(new HandlerHolder(predicate, handler));
return this;
}

public HttpHandler getFallbackHandler() {
return fallbackHandler;
}

public void setFallbackHandler(HttpHandler fallbackHandler) {
this.fallbackHandler = fallbackHandler;
}

private static class RoutingMatch {

final List<HandlerHolder> predicatedHandlers = new CopyOnWriteArrayList<HandlerHolder>();
volatile HttpHandler defaultHandler;

}

private static class HandlerHolder {
final Predicate predicate;
final HttpHandler handler;

private HandlerHolder(Predicate predicate, HttpHandler handler) {
this.predicate = predicate;
this.handler = handler;
}
}

}
Expand Up @@ -3,6 +3,7 @@
import io.undertow.server.HttpHandler;
import io.undertow.server.HttpServerExchange;
import io.undertow.util.AttachmentKey;
import io.undertow.util.PathTemplateMatch;
import io.undertow.util.PathTemplateMatcher;

import java.util.Map;
Expand All @@ -17,6 +18,10 @@ public class PathTemplateHandler implements HttpHandler {

private final boolean rewriteQueryParameters;

/**
* @see io.undertow.util.PathTemplateMatch#ATTACHMENT_KEY
*/
@Deprecated
public static final AttachmentKey<PathTemplateMatch> PATH_TEMPLATE_MATCH = AttachmentKey.create(PathTemplateMatch.class);

private final PathTemplateMatcher<HttpHandler> pathTemplateMatcher = new PathTemplateMatcher<HttpHandler>();
Expand All @@ -38,6 +43,7 @@ public void handleRequest(HttpServerExchange exchange) throws Exception {
return;
}
exchange.putAttachment(PATH_TEMPLATE_MATCH, new PathTemplateMatch(match.getMatchedTemplate(), match.getParameters()));
exchange.putAttachment(io.undertow.util.PathTemplateMatch.ATTACHMENT_KEY, new io.undertow.util.PathTemplateMatch(match.getMatchedTemplate(), match.getParameters()));
if (rewriteQueryParameters) {
for (Map.Entry<String, String> entry : match.getParameters().entrySet()) {
exchange.addQueryParam(entry.getKey(), entry.getValue());
Expand All @@ -56,6 +62,10 @@ public PathTemplateHandler remove(final String uriTemplate) {
return this;
}

/**
* @see io.undertow.util.PathTemplateMatch
*/
@Deprecated
public static final class PathTemplateMatch {
private final String matchedTemplate;
private final Map<String, String> parameters;
Expand Down
30 changes: 30 additions & 0 deletions core/src/main/java/io/undertow/util/PathTemplateMatch.java
@@ -0,0 +1,30 @@
package io.undertow.util;

import java.util.Map;

/**
* The result of a path template match.
*
* @author Stuart Douglas
*/
public class PathTemplateMatch {

public static final AttachmentKey<PathTemplateMatch> ATTACHMENT_KEY = AttachmentKey.create(PathTemplateMatch.class);

private final String matchedTemplate;
private final Map<String, String> parameters;

public PathTemplateMatch(String matchedTemplate, Map<String, String> parameters) {
this.matchedTemplate = matchedTemplate;
this.parameters = parameters;
}

public String getMatchedTemplate() {
return matchedTemplate;
}

public Map<String, String> getParameters() {
return parameters;
}

}
32 changes: 18 additions & 14 deletions core/src/main/java/io/undertow/util/PathTemplateMatcher.java
Expand Up @@ -137,7 +137,7 @@ private synchronized PathTemplateMatcher<T> remove(PathTemplate template) {
Set<PathTemplateHolder> values = pathTemplateMap.get(trimBase(template));
Set<PathTemplateHolder> newValues;
if (values == null) {
newValues = new TreeSet<PathTemplateHolder>();
return this;
} else {
newValues = new TreeSet<PathTemplateHolder>(values);
}
Expand All @@ -158,23 +158,27 @@ private synchronized PathTemplateMatcher<T> remove(PathTemplate template) {
return this;
}

public static class PathMatchResult<T> {
private final Map<String, String> parameters;
private final String matchedTemplate;
private final T value;

public PathMatchResult(Map<String, String> parameters, String matchedTemplate, T value) {
this.parameters = parameters;
this.matchedTemplate = matchedTemplate;
this.value = value;
public synchronized T get(String template) {
PathTemplate pathTemplate = PathTemplate.create(template);
Set<PathTemplateHolder> values = pathTemplateMap.get(trimBase(pathTemplate));
if(values == null) {
return null;
}

public Map<String, String> getParameters() {
return parameters;
for (PathTemplateHolder next : values) {
if (next.template.getTemplateString().equals(template)) {
return next.value;
}
}
return null;
}

public static class PathMatchResult<T> extends PathTemplateMatch {
private final T value;

public String getMatchedTemplate() {
return matchedTemplate;
public PathMatchResult(Map<String, String> parameters, String matchedTemplate, T value) {
super(matchedTemplate, parameters);
this.value = value;
}

public T getValue() {
Expand Down

0 comments on commit 04385f2

Please sign in to comment.