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