View Javadoc

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