package org.jboss.resteasy.core.registry;
import org.jboss.resteasy.core.ResourceMethodInvoker;
import org.jboss.resteasy.plugins.server.servlet.ResteasyContextParameters;
import org.jboss.resteasy.spi.HttpRequest;
import org.jboss.resteasy.spi.ResourceInvoker;
import javax.ws.rs.core.MultivaluedHashMap;
import javax.ws.rs.core.MultivaluedMap;
import java.lang.reflect.Method;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import static org.jboss.resteasy.core.registry.SegmentNode.RESTEASY_CHOSEN_ACCEPT;
public class RootNode
{
protected SegmentNode root = new SegmentNode("");
protected int size = 0;
protected MultivaluedMap<String, MethodExpression> bounded = new MultivaluedHashMap<String, MethodExpression>();
protected ConcurrentHashMap<MatchCache.Key, MatchCache> cache = new ConcurrentHashMap<>();
private static int CACHE_SIZE = 2048;
private static boolean CACHE = true;
static
{
if (System.getSecurityManager() == null) {
CACHE = Boolean.parseBoolean(System.getProperty(ResteasyContextParameters.RESTEASY_MATCH_CACHE_ENABLED, "true"));
CACHE_SIZE = Integer.getInteger(ResteasyContextParameters.RESTEASY_MATCH_CACHE_SIZE, 2048);
} else {
CACHE = AccessController.doPrivileged((PrivilegedAction<Boolean>) () ->
Boolean.parseBoolean(System.getProperty(ResteasyContextParameters.RESTEASY_MATCH_CACHE_ENABLED, "true")));
CACHE_SIZE = AccessController.doPrivileged((PrivilegedAction<Integer>) () ->
Integer.getInteger(ResteasyContextParameters.RESTEASY_MATCH_CACHE_SIZE, 2048));
}
}
public int getSize()
{
return size;
}
public MultivaluedMap<String, ResourceInvoker> getBounded()
{
MultivaluedHashMap<String, ResourceInvoker> rtn = new MultivaluedHashMap<String, ResourceInvoker>();
for (Map.Entry<String, List<MethodExpression>> entry : bounded.entrySet())
{
for (MethodExpression exp : entry.getValue())
{
rtn.add(entry.getKey(), exp.getInvoker());
}
}
return rtn;
}
public ResourceInvoker match(HttpRequest request, int start)
{
if (!CACHE || (request.getHttpHeaders().getMediaType() !=null && !request.getHttpHeaders().getMediaType().getParameters().isEmpty())) {
return root.match(request, start).invoker;
}
MatchCache.Key key = new MatchCache.Key(request, start);
MatchCache match = cache.get(key);
if (match != null) {
request.setAttribute(RESTEASY_CHOSEN_ACCEPT, match.chosen);
} else {
match = root.match(request, start);
if (match.match != null && match.match.expression.getNumGroups() == 0 && match.invoker instanceof ResourceMethodInvoker) {
match.match = null;
if (cache.size() >= CACHE_SIZE) {
cache.clear();
}
cache.putIfAbsent(key, match);
}
}
return match.invoker;
}
public void removeBinding(String path, Method method)
{
List<MethodExpression> expressions = bounded.get(path);
if (expressions == null) return;
for (MethodExpression expression : expressions)
{
ResourceInvoker invoker = expression.getInvoker();
if (invoker.getMethod().equals(method))
{
expression.parent.targets.remove(expression);
expressions.remove(expression);
if (expressions.size() == 0) bounded.remove(path);
size--;
if (invoker instanceof ResourceMethodInvoker)
{
((ResourceMethodInvoker)invoker).cleanup();
}
return;
}
}
}
public void addInvoker(String path, ResourceInvoker invoker)
{
MethodExpression expression = addExpression(path, invoker);
size++;
bounded.add(path, expression);
}
protected MethodExpression addExpression(String path, ResourceInvoker invoker)
{
if (path.startsWith("/")) path = path.substring(1);
if (path.endsWith("/")) path = path.substring(0, path.length() - 1);
if ("".equals(path))
{
if (invoker instanceof ResourceMethodInvoker)
{
MethodExpression expression = new MethodExpression(root, "", invoker);
root.addExpression(expression);
return expression;
}
else
{
MethodExpression expression = new MethodExpression(root, "", invoker, "(.*)");
root.addExpression(expression);
return expression;
}
}
MethodExpression exp;
int expidx = path.indexOf('{');
if (expidx > -1)
{
int i =expidx;
while (i - 1 > -1)
{
if (path.charAt(i - 1) == '/')
{
break;
}
i--;
}
String staticPath = null;
if (i > 0) staticPath = path.substring(0, i - 1);
SegmentNode node = root;
if (staticPath != null)
{
String[] split = staticPath.split("/");
for (String segment : split)
{
SegmentNode tmp = node.children.get(segment);
if (tmp == null)
{
tmp = new SegmentNode(segment);
node.children.put(segment, tmp);
}
node = tmp;
}
}
if (invoker instanceof ResourceMethodInvoker)
{
exp = new MethodExpression(node, path, invoker);
}
else
{
exp = new MethodExpression(node, path, invoker, "(/.+)?");
}
node.addExpression(exp);
}
else
{
String[] split = path.split("/");
SegmentNode node = root;
for (String segment : split)
{
SegmentNode tmp = node.children.get(segment);
if (tmp == null)
{
tmp = new SegmentNode(segment);
node.children.put(segment, tmp);
}
node = tmp;
}
if (invoker instanceof ResourceMethodInvoker)
{
exp = new MethodExpression(node, path, invoker);
node.addExpression(exp);
}
else
{
exp = new MethodExpression(node, path, invoker, "(.*)");
node.addExpression(exp);
}
}
return exp;
}
}