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  import java.util.HashMap;
23  import java.util.Map;
24  
25  import javax.servlet.ServletException;
26  import javax.servlet.http.HttpServletRequest;
27  import javax.servlet.http.HttpServletResponse;
28  
29  import org.eclipse.jetty.http.PathMap;
30  import org.eclipse.jetty.server.AsyncContinuation;
31  import org.eclipse.jetty.server.Handler;
32  import org.eclipse.jetty.server.HandlerContainer;
33  import org.eclipse.jetty.server.Request;
34  import org.eclipse.jetty.util.LazyList;
35  import org.eclipse.jetty.util.log.Log;
36  import org.eclipse.jetty.util.log.Logger;
37  
38  /* ------------------------------------------------------------ */
39  /** ContextHandlerCollection.
40   * 
41   * This {@link org.eclipse.jetty.server.handler.HandlerCollection} is creates a 
42   * {@link org.eclipse.jetty.http.PathMap} to it's contained handlers based
43   * on the context path and virtual hosts of any contained {@link org.eclipse.jetty.server.handler.ContextHandler}s.
44   * The contexts do not need to be directly contained, only children of the contained handlers.
45   * Multiple contexts may have the same context path and they are called in order until one
46   * handles the request.  
47   * 
48   * @org.apache.xbean.XBean element="contexts"
49   */
50  public class ContextHandlerCollection extends HandlerCollection
51  {
52      private static final Logger LOG = Log.getLogger(ContextHandlerCollection.class);
53   
54      private volatile PathMap _contextMap;
55      private Class<? extends ContextHandler> _contextClass = ContextHandler.class;
56      
57      /* ------------------------------------------------------------ */
58      public ContextHandlerCollection()
59      {
60          super(true);
61      }
62  
63  
64      /* ------------------------------------------------------------ */
65      /**
66       * Remap the context paths.
67       */
68      public void mapContexts()
69      {
70          PathMap contextMap = new PathMap();
71          Handler[] branches = getHandlers();
72          
73          
74          for (int b=0;branches!=null && b<branches.length;b++)
75          {
76              Handler[] handlers=null;
77              
78              if (branches[b] instanceof ContextHandler)
79              {
80                  handlers = new Handler[]{ branches[b] };
81              }
82              else if (branches[b] instanceof HandlerContainer)
83              {
84                  handlers = ((HandlerContainer)branches[b]).getChildHandlersByClass(ContextHandler.class);
85              }
86              else 
87                  continue;
88              
89              for (int i=0;i<handlers.length;i++)
90              {
91                  ContextHandler handler=(ContextHandler)handlers[i];
92  
93                  String contextPath=handler.getContextPath();
94  
95                  if (contextPath==null || contextPath.indexOf(',')>=0 || contextPath.startsWith("*"))
96                      throw new IllegalArgumentException ("Illegal context spec:"+contextPath);
97  
98                  if(!contextPath.startsWith("/"))
99                      contextPath='/'+contextPath;
100 
101                 if (contextPath.length()>1)
102                 {
103                     if (contextPath.endsWith("/"))
104                         contextPath+="*";
105                     else if (!contextPath.endsWith("/*"))
106                         contextPath+="/*";
107                 }
108 
109                 Object contexts=contextMap.get(contextPath);
110                 String[] vhosts=handler.getVirtualHosts();
111 
112                 
113                 if (vhosts!=null && vhosts.length>0)
114                 {
115                     Map hosts;
116 
117                     if (contexts instanceof Map)
118                         hosts=(Map)contexts;
119                     else
120                     {
121                         hosts=new HashMap(); 
122                         hosts.put("*",contexts);
123                         contextMap.put(contextPath, hosts);
124                     }
125 
126                     for (int j=0;j<vhosts.length;j++)
127                     {
128                         String vhost=vhosts[j];
129                         contexts=hosts.get(vhost);
130                         contexts=LazyList.add(contexts,branches[b]);
131                         hosts.put(vhost,contexts);
132                     }
133                 }
134                 else if (contexts instanceof Map)
135                 {
136                     Map hosts=(Map)contexts;
137                     contexts=hosts.get("*");
138                     contexts= LazyList.add(contexts, branches[b]);
139                     hosts.put("*",contexts);
140                 }
141                 else
142                 {
143                     contexts= LazyList.add(contexts, branches[b]);
144                     contextMap.put(contextPath, contexts);
145                 }
146             }
147         }
148         _contextMap=contextMap;
149 
150     }
151     
152 
153     
154     /* ------------------------------------------------------------ */
155     /* 
156      * @see org.eclipse.jetty.server.server.handler.HandlerCollection#setHandlers(org.eclipse.jetty.server.server.Handler[])
157      */
158     @Override
159     public void setHandlers(Handler[] handlers)
160     {
161         _contextMap=null;
162         super.setHandlers(handlers);
163         if (isStarted())
164             mapContexts();
165     }
166 
167     /* ------------------------------------------------------------ */
168     @Override
169     protected void doStart() throws Exception
170     {
171         mapContexts();
172         super.doStart();
173     }
174     
175 
176     /* ------------------------------------------------------------ */
177     /* 
178      * @see org.eclipse.jetty.server.server.Handler#handle(java.lang.String, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, int)
179      */
180     @Override
181     public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
182     {
183         Handler[] handlers = getHandlers();
184         if (handlers==null || handlers.length==0)
185 	    return;
186 	
187 	AsyncContinuation async = baseRequest.getAsyncContinuation();
188 	if (async.isAsync())
189 	{
190 	    ContextHandler context=async.getContextHandler();
191 	    if (context!=null)
192 	    {
193 	        context.handle(target,baseRequest,request, response);
194 	        return;
195 	    }
196 	}
197 	
198 	// data structure which maps a request to a context; first-best match wins
199 	// { context path => 
200 	//     { virtual host => context } 
201 	// }
202 	PathMap map = _contextMap;
203 	if (map!=null && target!=null && target.startsWith("/"))
204 	{
205 	    // first, get all contexts matched by context path
206 	    Object contexts = map.getLazyMatches(target);
207 
208             for (int i=0; i<LazyList.size(contexts); i++)
209             {
210                 // then, match against the virtualhost of each context
211                 Map.Entry entry = (Map.Entry)LazyList.get(contexts, i);
212                 Object list = entry.getValue();
213 
214                 if (list instanceof Map)
215                 {
216                     Map hosts = (Map)list;
217                     String host = normalizeHostname(request.getServerName());
218            
219                     // explicitly-defined virtual hosts, most specific
220                     list=hosts.get(host);
221                     for (int j=0; j<LazyList.size(list); j++)
222                     {
223                         Handler handler = (Handler)LazyList.get(list,j);
224                         handler.handle(target,baseRequest, request, response);
225                         if (baseRequest.isHandled())
226                             return;
227                     }
228                     
229                     // wildcard for one level of names 
230                     list=hosts.get("*."+host.substring(host.indexOf(".")+1));
231                     for (int j=0; j<LazyList.size(list); j++)
232                     {
233                         Handler handler = (Handler)LazyList.get(list,j);
234                         handler.handle(target,baseRequest, request, response);
235                         if (baseRequest.isHandled())
236                             return;
237                     }
238                     
239                     // no virtualhosts defined for the context, least specific
240                     // will handle any request that does not match to a specific virtual host above
241                     list=hosts.get("*");
242                     for (int j=0; j<LazyList.size(list); j++)
243                     {
244                         Handler handler = (Handler)LazyList.get(list,j);
245                         handler.handle(target,baseRequest, request, response);
246                         if (baseRequest.isHandled())
247                             return;
248                     }
249                 }
250                 else
251                 {
252                     for (int j=0; j<LazyList.size(list); j++)
253                     {
254                         Handler handler = (Handler)LazyList.get(list,j);
255                         handler.handle(target,baseRequest, request, response);
256                         if (baseRequest.isHandled())
257                             return;
258                     }
259                 }
260 	    }
261 	}
262 	else
263 	{
264             // This may not work in all circumstances... but then I think it should never be called
265 	    for (int i=0;i<handlers.length;i++)
266 	    {
267 		handlers[i].handle(target,baseRequest, request, response);
268 		if ( baseRequest.isHandled())
269 		    return;
270 	    }
271 	}
272     }
273     
274     
275     /* ------------------------------------------------------------ */
276     /** Add a context handler.
277      * @param contextPath  The context path to add
278      * @return the ContextHandler just added
279      */
280     public ContextHandler addContext(String contextPath,String resourceBase) 
281     {
282         try
283         {
284             ContextHandler context = _contextClass.newInstance();
285             context.setContextPath(contextPath);
286             context.setResourceBase(resourceBase);
287             addHandler(context);
288             return context;
289         }
290         catch (Exception e)
291         {
292             LOG.debug(e);
293             throw new Error(e);
294         }
295     }
296 
297 
298 
299     /* ------------------------------------------------------------ */
300     /**
301      * @return The class to use to add new Contexts
302      */
303     public Class getContextClass()
304     {
305         return _contextClass;
306     }
307 
308 
309     /* ------------------------------------------------------------ */
310     /**
311      * @param contextClass The class to use to add new Contexts
312      */
313     public void setContextClass(Class contextClass)
314     {
315         if (contextClass ==null || !(ContextHandler.class.isAssignableFrom(contextClass)))
316             throw new IllegalArgumentException();
317         _contextClass = contextClass;
318     }
319     
320     /* ------------------------------------------------------------ */
321     private String normalizeHostname( String host )
322     {
323         if ( host == null )
324             return null;
325         
326         if ( host.endsWith( "." ) )
327             return host.substring( 0, host.length() -1);
328       
329         return host;
330     }
331     
332 }