final String requestURI = httpRequest.getRequestURI();
final String path = buildMatchablePath(httpRequest, baseUri, requestURI, true);
final String pathWithoutParams = buildMatchablePath(httpRequest, baseUri, requestURI, false);
// find the matching invoker
Invoker invoker = defaultInvoker;
Matcher matcher = null;
for (final Map.Entry<Pattern, Invoker> entry : invokers.entrySet()) {
final Pattern pattern = entry.getKey();
if ((matcher = pattern.matcher(path)).matches()) {
invoker = entry.getValue();
if (!entry.getKey().pattern().endsWith(".*")) {
break;
}
} else if ((matcher = pattern.matcher(pathWithoutParams)).matches()) {
invoker = entry.getValue(); // continue since that's a not perfect matching
}
}
// handle Content-Type, we could use a map but this is more efficient ATM and can still be overriden
boolean skipFiltering = false;
if (requestURI.endsWith(".css")) {
httpResponse.setHeader(CONTENT_TYPE, "text/css");
} else if (requestURI.endsWith(".js")) {
httpResponse.setHeader(CONTENT_TYPE, "application/javascript");
} else if (requestURI.endsWith(".png")) {
httpResponse.setHeader(CONTENT_TYPE, "image/png");
skipFiltering = true;
} else if (requestURI.endsWith(".gif")) {
httpResponse.setHeader(CONTENT_TYPE, "image/gif");
skipFiltering = true;
} else if (requestURI.endsWith(".jpg")) {
httpResponse.setHeader(CONTENT_TYPE, "image/jpeg");
skipFiltering = true;
} else if (requestURI.endsWith(".svg")) {
httpResponse.setHeader(CONTENT_TYPE, "image/svg+xml");
skipFiltering = true;
} else if (requestURI.endsWith(".eot")) {
httpResponse.setHeader(CONTENT_TYPE, "application/vnd.ms-fontobject");
skipFiltering = true;
} else if (requestURI.endsWith(".woff")) {
httpResponse.setHeader(CONTENT_TYPE, "application/font-woff");
skipFiltering = true;
} else if (requestURI.endsWith(".ttf") || requestURI.endsWith(".itf")) {
httpResponse.setHeader(CONTENT_TYPE, "application/octet-stream");
skipFiltering = true;
}
// resource, they are in the classloader and not in the webapp to ease the embedded case
if (pathWithoutParams.startsWith("/resources/")) {
byte[] bytes = cachedResources.get(pathWithoutParams);
if (bytes == null) {
final InputStream is;
if (!skipFiltering && invoker != defaultInvoker) { // resource is filtered so filtering it before caching it
final StringWriter writer = new StringWriter();
final PrintWriter printWriter = new PrintWriter(writer);
invoker.invoke(httpRequest, HttpServletResponse.class.cast(Proxy.newProxyInstance(classloader, new Class<?>[]{HttpServletResponse.class}, new InvocationHandler() {
@Override
public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
if ("getWriter".equals(method.getName())) {
return printWriter;
}
return method.invoke(httpResponse, args);
}
})), null);
is = new ByteArrayInputStream(writer.toString().getBytes());
} else {
is = classloader.getResourceAsStream(pathWithoutParams.substring(1));
}
if (is != null) {
ByteArrayOutputStream baos = ByteArrayOutputStream.class.cast(request.getAttribute("resourceCache"));
if (baos == null) {
baos = new ByteArrayOutputStream();
int i;
while ((i = is.read()) != -1) {
baos.write(i);
}
}
bytes = baos.toByteArray();
cachedResources.put(pathWithoutParams, bytes);
}
}
if (bytes != null) {
if (bytes.length == 0) {
httpResponse.setStatus(404);
} else {
httpResponse.getOutputStream().write(bytes);
}
return;
}
}
// delegate handling to the invoker if request is not a resource
if (invoker == null) {
error(response, null);
} else {
try {
invoker.invoke(httpRequest, httpResponse, matcher);
} catch (final Exception e) {
error(response, e);
}
}
}