Issue #3361 thread safe addHandler
Made Context mapping atomic with a volatile mapping Signed-off-by: Greg Wilkins <gregw@webtide.com>
This commit is contained in:
parent
b7e3d6ce5e
commit
49eadc5246
|
@ -24,8 +24,6 @@ import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
import java.util.concurrent.ConcurrentMap;
|
|
||||||
|
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
@ -59,8 +57,7 @@ public class ContextHandlerCollection extends HandlerCollection
|
||||||
{
|
{
|
||||||
private static final Logger LOG = Log.getLogger(ContextHandlerCollection.class);
|
private static final Logger LOG = Log.getLogger(ContextHandlerCollection.class);
|
||||||
|
|
||||||
private final ConcurrentMap<ContextHandler,Handler> _contextBranches = new ConcurrentHashMap<>();
|
private volatile Mapping _mapping;
|
||||||
private volatile Trie<Map.Entry<String,Branch[]>> _pathBranches;
|
|
||||||
private Class<? extends ContextHandler> _contextClass = ContextHandler.class;
|
private Class<? extends ContextHandler> _contextClass = ContextHandler.class;
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
|
@ -83,32 +80,32 @@ public class ContextHandlerCollection extends HandlerCollection
|
||||||
@ManagedOperation("update the mapping of context path to context")
|
@ManagedOperation("update the mapping of context path to context")
|
||||||
public void mapContexts()
|
public void mapContexts()
|
||||||
{
|
{
|
||||||
_contextBranches.clear();
|
mapContexts(getHandlers());
|
||||||
|
}
|
||||||
Handler[] handlers = getHandlers();
|
|
||||||
|
private void mapContexts(Handler[] handlers)
|
||||||
|
{
|
||||||
if (handlers==null)
|
if (handlers==null)
|
||||||
{
|
{
|
||||||
_pathBranches=new ArrayTernaryTrie<>(false,16);
|
_mapping = new Mapping(1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create map of contextPath to handler Branch
|
// Create map of contextPath to handler Branch
|
||||||
Map<String,Branch[]> map = new HashMap<>();
|
// A branch is a Handler that could contain 0 or more ContextHandlers
|
||||||
|
Map<String,Branch[]> path2Branches = new HashMap<>();
|
||||||
for (Handler handler:handlers)
|
for (Handler handler:handlers)
|
||||||
{
|
{
|
||||||
Branch branch=new Branch(handler);
|
Branch branch=new Branch(handler);
|
||||||
for (String contextPath : branch.getContextPaths())
|
for (String contextPath : branch.getContextPaths())
|
||||||
{
|
{
|
||||||
Branch[] branches=map.get(contextPath);
|
Branch[] branches=path2Branches.get(contextPath);
|
||||||
map.put(contextPath, ArrayUtil.addToArray(branches, branch, Branch.class));
|
path2Branches.put(contextPath, ArrayUtil.addToArray(branches, branch, Branch.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (ContextHandler context : branch.getContextHandlers())
|
|
||||||
_contextBranches.putIfAbsent(context, branch.getHandler());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort the branches so those with virtual hosts are considered before those without
|
// Sort the branches for each contextPath so those with virtual hosts are considered before those without
|
||||||
for (Map.Entry<String,Branch[]> entry: map.entrySet())
|
for (Map.Entry<String,Branch[]> entry: path2Branches.entrySet())
|
||||||
{
|
{
|
||||||
Branch[] branches=entry.getValue();
|
Branch[] branches=entry.getValue();
|
||||||
Branch[] sorted=new Branch[branches.length];
|
Branch[] sorted=new Branch[branches.length];
|
||||||
|
@ -124,13 +121,13 @@ public class ContextHandlerCollection extends HandlerCollection
|
||||||
|
|
||||||
// Loop until we have a big enough trie to hold all the context paths
|
// Loop until we have a big enough trie to hold all the context paths
|
||||||
int capacity=512;
|
int capacity=512;
|
||||||
Trie<Map.Entry<String,Branch[]>> trie;
|
Mapping mapping;
|
||||||
loop: while(true)
|
loop: while(true)
|
||||||
{
|
{
|
||||||
trie=new ArrayTernaryTrie<>(false,capacity);
|
mapping = new Mapping(capacity);
|
||||||
for (Map.Entry<String,Branch[]> entry: map.entrySet())
|
for (Map.Entry<String,Branch[]> entry: path2Branches.entrySet())
|
||||||
{
|
{
|
||||||
if (!trie.put(entry.getKey().substring(1),entry))
|
if (!mapping._pathBranches.put(entry.getKey().substring(1),entry))
|
||||||
{
|
{
|
||||||
capacity+=512;
|
capacity+=512;
|
||||||
continue loop;
|
continue loop;
|
||||||
|
@ -141,10 +138,22 @@ public class ContextHandlerCollection extends HandlerCollection
|
||||||
|
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
{
|
{
|
||||||
for (String ctx : trie.keySet())
|
for (String ctx : mapping._pathBranches.keySet())
|
||||||
LOG.debug("{}->{}",ctx,Arrays.asList(trie.get(ctx).getValue()));
|
LOG.debug("{}->{}",ctx,Arrays.asList(mapping._pathBranches.get(ctx).getValue()));
|
||||||
}
|
}
|
||||||
_pathBranches=trie;
|
|
||||||
|
// add new context branches to concurrent map
|
||||||
|
for (Branch[] branches: path2Branches.values())
|
||||||
|
{
|
||||||
|
for (Branch branch : branches)
|
||||||
|
{
|
||||||
|
for (ContextHandler context : branch.getContextHandlers())
|
||||||
|
mapping._contextBranches.put(context, branch.getHandler());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update volatile mapping
|
||||||
|
_mapping = mapping;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
|
@ -156,7 +165,7 @@ public class ContextHandlerCollection extends HandlerCollection
|
||||||
{
|
{
|
||||||
super.setHandlers(handlers);
|
super.setHandlers(handlers);
|
||||||
if (isStarted())
|
if (isStarted())
|
||||||
mapContexts();
|
mapContexts(handlers);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
|
@ -167,6 +176,13 @@ public class ContextHandlerCollection extends HandlerCollection
|
||||||
super.doStart();
|
super.doStart();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------ */
|
||||||
|
@Override
|
||||||
|
protected void doStop() throws Exception
|
||||||
|
{
|
||||||
|
super.doStop();
|
||||||
|
_mapping = null;
|
||||||
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/*
|
/*
|
||||||
|
@ -175,13 +191,17 @@ public class ContextHandlerCollection extends HandlerCollection
|
||||||
@Override
|
@Override
|
||||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||||
{
|
{
|
||||||
|
Mapping mapping = _mapping;
|
||||||
|
if (mapping==null)
|
||||||
|
return;
|
||||||
|
|
||||||
HttpChannelState async = baseRequest.getHttpChannelState();
|
HttpChannelState async = baseRequest.getHttpChannelState();
|
||||||
if (async.isAsync())
|
if (async.isAsync())
|
||||||
{
|
{
|
||||||
ContextHandler context=async.getContextHandler();
|
ContextHandler context=async.getContextHandler();
|
||||||
if (context!=null)
|
if (context!=null)
|
||||||
{
|
{
|
||||||
Handler branch = _contextBranches.get(context);
|
Handler branch = mapping._contextBranches.get(context);
|
||||||
|
|
||||||
if (branch==null)
|
if (branch==null)
|
||||||
context.handle(target,baseRequest,request, response);
|
context.handle(target,baseRequest,request, response);
|
||||||
|
@ -193,7 +213,7 @@ public class ContextHandlerCollection extends HandlerCollection
|
||||||
|
|
||||||
if (target.startsWith("/"))
|
if (target.startsWith("/"))
|
||||||
{
|
{
|
||||||
Trie<Map.Entry<String,Branch[]>> pathBranches = _pathBranches;
|
Trie<Map.Entry<String,Branch[]>> pathBranches = mapping._pathBranches;
|
||||||
if (pathBranches==null)
|
if (pathBranches==null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -340,5 +360,17 @@ public class ContextHandlerCollection extends HandlerCollection
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------ */
|
||||||
|
/* ------------------------------------------------------------ */
|
||||||
|
/* ------------------------------------------------------------ */
|
||||||
|
private static class Mapping
|
||||||
|
{
|
||||||
|
private final Map<ContextHandler,Handler> _contextBranches = new HashMap<>();
|
||||||
|
private final Trie<Map.Entry<String,Branch[]>> _pathBranches;
|
||||||
|
|
||||||
|
Mapping(int capacity)
|
||||||
|
{
|
||||||
|
_pathBranches = new ArrayTernaryTrie(false, capacity);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue