package org.deftserver.web;
import java.util.Map;
import java.util.regex.Pattern;
import org.deftserver.util.HttpUtil;
import org.deftserver.web.handler.BadRequestRequestHandler;
import org.deftserver.web.handler.ForbiddenRequestHandler;
import org.deftserver.web.handler.NotFoundRequestHandler;
import org.deftserver.web.handler.RequestHandler;
import org.deftserver.web.handler.StaticContentHandler;
import org.deftserver.web.http.HttpRequest;
import com.google.common.collect.ImmutableMap;
public class Application {
/**
* "Normal/Absolute" (non group capturing) RequestHandlers
* e.g. "/", "/persons"
*/
private final ImmutableMap<String, RequestHandler> absoluteHandlers;
/**
* Group capturing RequestHandlers
* e.g. "/persons/([0-9]+)", "/persons/(\\d{1,3})"
*/
private final ImmutableMap<String, RequestHandler> capturingHandlers;
/**
* A mapping between group capturing RequestHandlers and their corresponding pattern ( e.g. "([0-9]+)" )
*/
private final ImmutableMap<RequestHandler, Pattern> patterns;
/**
* The directory where static content (files) will be served from.
*/
private String staticContentDir;
public Application(Map<String, RequestHandler> handlers) {
ImmutableMap.Builder<String, RequestHandler> builder = new ImmutableMap.Builder<String, RequestHandler>();
ImmutableMap.Builder<String, RequestHandler> capturingBuilder = new ImmutableMap.Builder<String, RequestHandler>();
ImmutableMap.Builder<RequestHandler, Pattern> patternsBuilder = new ImmutableMap.Builder<RequestHandler, Pattern>();
for (String path : handlers.keySet()) {
int index = path.lastIndexOf("/");
String group = path.substring(index+1, path.length());
if (containsCapturingGroup(group)) {
// path ends with capturing group, e.g path == "/person/([0-9]+)"
capturingBuilder.put(path.substring(0, index+1), handlers.get(path));
patternsBuilder.put(handlers.get(path), Pattern.compile(group));
} else {
// "normal" path, e.g. path == "/"
builder.put(path, handlers.get(path));
}
}
absoluteHandlers = builder.build();
capturingHandlers = capturingBuilder.build();
patterns = patternsBuilder.build();
}
/**
*
* @param path Requested path
* @return Returns the {@link RequestHandler} associated with the given path. If no mapping exists a
* {@link NotFoundRequestHandler} is returned.
*/
private RequestHandler getHandler(String path) {
RequestHandler rh = absoluteHandlers.get(path);
if (rh == null) {
// path could contain capturing groups which we could have a handler associated with.
rh = getCapturingHandler(path);
if (rh == null) {
// path could be prefixed with the 'static content directory'
rh = getStaticContentHandler(path);
}
}
return rh != null ? rh : NotFoundRequestHandler.getInstance(); // TODO RS store in a final field for improved performance?
}
public RequestHandler getHandler(HttpRequest request) {
if (!HttpUtil.verifyRequest(request)) {
return BadRequestRequestHandler.getInstance();
}
// if @Authenticated annotation is present, make sure that the request/user is authenticated
// (i.e RequestHandler.getCurrentUser() != null).
RequestHandler rh = getHandler(request.getRequestedPath());;
if (rh.isMethodAuthenticated(request.getMethod()) && rh.getCurrentUser(request) == null) {
return ForbiddenRequestHandler.getInstance();
}
return rh;
}
private boolean containsCapturingGroup(String group) {
boolean containsGroup = group.matches("^\\(.*\\)$");
Pattern.compile(group); // throws PatternSyntaxException if group is malformed regular expression
return containsGroup;
}
private RequestHandler getCapturingHandler(String path) {
int index = path.lastIndexOf("/");
if (index != -1) {
String init = path.substring(0, index+1); // path without its last segment
String group = path.substring(index+1, path.length());
RequestHandler handler = capturingHandlers.get(init);
if (handler != null) {
Pattern regex = patterns.get(handler);
if (regex.matcher(group).matches()) {
return handler;
}
}
}
return null;
}
private RequestHandler getStaticContentHandler(String path) {
if (staticContentDir == null || path.length() <= staticContentDir.length()) {
return null; // quick reject (no static dir or simple contradiction)
}
if (path.substring(1).startsWith(staticContentDir)) {
return StaticContentHandler.getInstance();
} else {
return null;
}
}
public void setStaticContentDir(String scd) {
this.staticContentDir = scd;
}
}