View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 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.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.server.handler;
20  
21  import java.io.IOException;
22  
23  import javax.servlet.ServletException;
24  import javax.servlet.http.HttpServletRequest;
25  import javax.servlet.http.HttpServletResponse;
26  
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;
38  
39  /* ------------------------------------------------------------ */
40  /** ContextHandlerCollection.
41   *
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.
48   *
49   */
50  @ManagedObject("Context Handler Collection")
51  public class ContextHandlerCollection extends HandlerCollection
52  {
53      private static final Logger LOG = Log.getLogger(ContextHandlerCollection.class);
54  
55      private volatile Trie<ContextHandler[]> _contexts;
56      private Class<? extends ContextHandler> _contextClass = ContextHandler.class;
57  
58      /* ------------------------------------------------------------ */
59      public ContextHandlerCollection()
60      {
61          super(true);
62      }
63  
64  
65      /* ------------------------------------------------------------ */
66      /**
67       * Remap the context paths.
68       */
69      @ManagedOperation("update the mapping of context path to context")
70      public void mapContexts()
71      {
72          int capacity=512;
73          
74          // Loop until we have a big enough trie to hold all the context paths
75          Trie<ContextHandler[]> trie;
76          loop: while(true)
77          {
78              trie=new ArrayTernaryTrie<>(false,capacity);
79  
80              Handler[] branches = getHandlers();
81  
82              // loop over each group of contexts
83              for (int b=0;branches!=null && b<branches.length;b++)
84              {
85                  Handler[] handlers=null;
86  
87                  if (branches[b] instanceof ContextHandler)
88                  {
89                      handlers = new Handler[]{ branches[b] };
90                  }
91                  else if (branches[b] instanceof HandlerContainer)
92                  {
93                      handlers = ((HandlerContainer)branches[b]).getChildHandlersByClass(ContextHandler.class);
94                  }
95                  else
96                      continue;
97  
98                  // for each context handler in a group
99                  for (int i=0;handlers!=null && i<handlers.length;i++)
100                 {
101                     ContextHandler handler=(ContextHandler)handlers[i];
102                     String contextPath=handler.getContextPath().substring(1);
103                     ContextHandler[] contexts=trie.get(contextPath);
104                     
105                     if (!trie.put(contextPath,ArrayUtil.addToArray(contexts,handler,ContextHandler.class)))
106                     {
107                         capacity+=512;
108                         continue loop;
109                     }
110                 }
111             }
112             
113             break;
114         }
115         
116         // Sort the contexts so those with virtual hosts are considered before those without
117         for (String ctx : trie.keySet())
118         {
119             ContextHandler[] contexts=trie.get(ctx);
120             ContextHandler[] sorted=new ContextHandler[contexts.length];
121             int i=0;
122             for (ContextHandler handler:contexts)
123                 if (handler.getVirtualHosts()!=null && handler.getVirtualHosts().length>0)
124                     sorted[i++]=handler;
125             for (ContextHandler handler:contexts)
126                 if (handler.getVirtualHosts()==null || handler.getVirtualHosts().length==0)
127                     sorted[i++]=handler;
128             trie.put(ctx,sorted);
129         }
130 
131         //for (String ctx : trie.keySet())
132         //    System.err.printf("'%s'->%s%n",ctx,Arrays.asList(trie.get(ctx)));
133         _contexts=trie;
134     }
135 
136 
137     /* ------------------------------------------------------------ */
138     /*
139      * @see org.eclipse.jetty.server.server.handler.HandlerCollection#setHandlers(org.eclipse.jetty.server.server.Handler[])
140      */
141     @Override
142     public void setHandlers(Handler[] handlers)
143     {
144         super.setHandlers(handlers);
145         if (isStarted())
146             mapContexts();
147     }
148 
149     /* ------------------------------------------------------------ */
150     @Override
151     protected void doStart() throws Exception
152     {
153         mapContexts();
154         super.doStart();
155     }
156 
157 
158     /* ------------------------------------------------------------ */
159     /*
160      * @see org.eclipse.jetty.server.server.Handler#handle(java.lang.String, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, int)
161      */
162     @Override
163     public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
164     {
165         Handler[] handlers = getHandlers();
166         if (handlers==null || handlers.length==0)
167 	    return;
168 
169 	HttpChannelState async = baseRequest.getHttpChannelState();
170 	if (async.isAsync())
171 	{
172 	    ContextHandler context=async.getContextHandler();
173 	    if (context!=null)
174 	    {
175 	        context.handle(target,baseRequest,request, response);
176 	        return;
177 	    }
178 	}
179 
180 	// data structure which maps a request to a context; first-best match wins
181 	// { context path => [ context ] }
182 	// }
183 	if (target.startsWith("/"))
184 	{
185 	    int limit = target.length()-1;
186 
187 	    while (limit>=0)
188 	    {
189 	        // Get best match
190 	        ContextHandler[] contexts = _contexts.getBest(target,1,limit);
191 	        if (contexts==null)
192 	            break;
193 
194 	        int l=contexts[0].getContextPath().length();
195 	        if (l==1 || target.length()==l || target.charAt(l)=='/')
196 	        {
197 	            for (ContextHandler handler : contexts)
198 	            {
199 	                handler.handle(target,baseRequest, request, response);
200 	                if (baseRequest.isHandled())
201 	                    return;
202 	            }
203 	        }
204 	        
205 	        limit=l-2;
206 	    }
207 	}
208 	else
209 	{
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++)
212 	    {
213 		handlers[i].handle(target,baseRequest, request, response);
214 		if ( baseRequest.isHandled())
215 		    return;
216 	    }
217 	}
218     }
219 
220 
221     /* ------------------------------------------------------------ */
222     /** Add a context handler.
223      * @param contextPath  The context path to add
224      * @return the ContextHandler just added
225      */
226     public ContextHandler addContext(String contextPath,String resourceBase)
227     {
228         try
229         {
230             ContextHandler context = _contextClass.newInstance();
231             context.setContextPath(contextPath);
232             context.setResourceBase(resourceBase);
233             addHandler(context);
234             return context;
235         }
236         catch (Exception e)
237         {
238             LOG.debug(e);
239             throw new Error(e);
240         }
241     }
242 
243 
244 
245     /* ------------------------------------------------------------ */
246     /**
247      * @return The class to use to add new Contexts
248      */
249     public Class<?> getContextClass()
250     {
251         return _contextClass;
252     }
253 
254 
255     /* ------------------------------------------------------------ */
256     /**
257      * @param contextClass The class to use to add new Contexts
258      */
259     public void setContextClass(Class<? extends ContextHandler> contextClass)
260     {
261         if (contextClass ==null || !(ContextHandler.class.isAssignableFrom(contextClass)))
262             throw new IllegalArgumentException();
263         _contextClass = contextClass;
264     }
265 
266 
267 }