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:
Greg Wilkins 2019-03-19 17:58:27 +11:00
parent b7e3d6ce5e
commit 49eadc5246
1 changed files with 58 additions and 26 deletions

View File

@ -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);
}
}
} }