2 // ========================================================================
3 // Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
4 // ------------------------------------------------------------------------
5 // All rights reserved. This program and the accompanying materials
6 // are made available under the terms of the Eclipse Public License v1.0
7 // and Apache License v2.0 which accompanies this distribution.
9 // The Eclipse Public License is available at
10 // http://www.eclipse.org/legal/epl-v10.html
12 // The Apache License v2.0 is available at
13 // http://www.opensource.org/licenses/apache2.0.php
15 // You may elect to redistribute this code under either of these licenses.
16 // ========================================================================
19 package org.eclipse.jetty.server.handler;
21 import java.io.IOException;
23 import javax.servlet.ServletException;
24 import javax.servlet.http.HttpServletRequest;
25 import javax.servlet.http.HttpServletResponse;
27 import org.eclipse.jetty.server.Handler;
28 import org.eclipse.jetty.server.HandlerContainer;
29 import org.eclipse.jetty.server.HttpChannelState;
30 import org.eclipse.jetty.server.Request;
31 import org.eclipse.jetty.util.ArrayTernaryTrie;
32 import org.eclipse.jetty.util.ArrayUtil;
33 import org.eclipse.jetty.util.Trie;
34 import org.eclipse.jetty.util.annotation.ManagedObject;
35 import org.eclipse.jetty.util.annotation.ManagedOperation;
36 import org.eclipse.jetty.util.log.Log;
37 import org.eclipse.jetty.util.log.Logger;
39 /* ------------------------------------------------------------ */
40 /** ContextHandlerCollection.
42 * This {@link org.eclipse.jetty.server.handler.HandlerCollection} is creates a
43 * {@link org.eclipse.jetty.http.PathMap} to it's contained handlers based
44 * on the context path and virtual hosts of any contained {@link org.eclipse.jetty.server.handler.ContextHandler}s.
45 * The contexts do not need to be directly contained, only children of the contained handlers.
46 * Multiple contexts may have the same context path and they are called in order until one
47 * handles the request.
50 @ManagedObject("Context Handler Collection")
51 public class ContextHandlerCollection extends HandlerCollection
53 private static final Logger LOG = Log.getLogger(ContextHandlerCollection.class);
55 private volatile Trie<ContextHandler[]> _contexts;
56 private Class<? extends ContextHandler> _contextClass = ContextHandler.class;
58 /* ------------------------------------------------------------ */
59 public ContextHandlerCollection()
65 /* ------------------------------------------------------------ */
67 * Remap the context paths.
69 @ManagedOperation("update the mapping of context path to context")
70 public void mapContexts()
74 // Loop until we have a big enough trie to hold all the context paths
75 Trie<ContextHandler[]> trie;
78 trie=new ArrayTernaryTrie<>(false,capacity);
80 Handler[] branches = getHandlers();
82 // loop over each group of contexts
83 for (int b=0;branches!=null && b<branches.length;b++)
85 Handler[] handlers=null;
87 if (branches[b] instanceof ContextHandler)
89 handlers = new Handler[]{ branches[b] };
91 else if (branches[b] instanceof HandlerContainer)
93 handlers = ((HandlerContainer)branches[b]).getChildHandlersByClass(ContextHandler.class);
98 // for each context handler in a group
99 for (int i=0;handlers!=null && i<handlers.length;i++)
101 ContextHandler handler=(ContextHandler)handlers[i];
102 String contextPath=handler.getContextPath().substring(1);
103 ContextHandler[] contexts=trie.get(contextPath);
105 if (!trie.put(contextPath,ArrayUtil.addToArray(contexts,handler,ContextHandler.class)))
116 // Sort the contexts so those with virtual hosts are considered before those without
117 for (String ctx : trie.keySet())
119 ContextHandler[] contexts=trie.get(ctx);
120 ContextHandler[] sorted=new ContextHandler[contexts.length];
122 for (ContextHandler handler:contexts)
123 if (handler.getVirtualHosts()!=null && handler.getVirtualHosts().length>0)
125 for (ContextHandler handler:contexts)
126 if (handler.getVirtualHosts()==null || handler.getVirtualHosts().length==0)
128 trie.put(ctx,sorted);
131 //for (String ctx : trie.keySet())
132 // System.err.printf("'%s'->%s%n",ctx,Arrays.asList(trie.get(ctx)));
137 /* ------------------------------------------------------------ */
139 * @see org.eclipse.jetty.server.server.handler.HandlerCollection#setHandlers(org.eclipse.jetty.server.server.Handler[])
142 public void setHandlers(Handler[] handlers)
144 super.setHandlers(handlers);
149 /* ------------------------------------------------------------ */
151 protected void doStart() throws Exception
158 /* ------------------------------------------------------------ */
160 * @see org.eclipse.jetty.server.server.Handler#handle(java.lang.String, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, int)
163 public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
165 Handler[] handlers = getHandlers();
166 if (handlers==null || handlers.length==0)
169 HttpChannelState async = baseRequest.getHttpChannelState();
172 ContextHandler context=async.getContextHandler();
175 context.handle(target,baseRequest,request, response);
180 // data structure which maps a request to a context; first-best match wins
181 // { context path => [ context ] }
183 if (target.startsWith("/"))
185 int limit = target.length()-1;
190 ContextHandler[] contexts = _contexts.getBest(target,1,limit);
194 int l=contexts[0].getContextPath().length();
195 if (l==1 || target.length()==l || target.charAt(l)=='/')
197 for (ContextHandler handler : contexts)
199 handler.handle(target,baseRequest, request, response);
200 if (baseRequest.isHandled())
210 // This may not work in all circumstances... but then I think it should never be called
211 for (int i=0;i<handlers.length;i++)
213 handlers[i].handle(target,baseRequest, request, response);
214 if ( baseRequest.isHandled())
221 /* ------------------------------------------------------------ */
222 /** Add a context handler.
223 * @param contextPath The context path to add
224 * @return the ContextHandler just added
226 public ContextHandler addContext(String contextPath,String resourceBase)
230 ContextHandler context = _contextClass.newInstance();
231 context.setContextPath(contextPath);
232 context.setResourceBase(resourceBase);
245 /* ------------------------------------------------------------ */
247 * @return The class to use to add new Contexts
249 public Class<?> getContextClass()
251 return _contextClass;
255 /* ------------------------------------------------------------ */
257 * @param contextClass The class to use to add new Contexts
259 public void setContextClass(Class<? extends ContextHandler> contextClass)
261 if (contextClass ==null || !(ContextHandler.class.isAssignableFrom(contextClass)))
262 throw new IllegalArgumentException();
263 _contextClass = contextClass;