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.HttpConnection;
29  import org.eclipse.jetty.server.Request;
30  import org.eclipse.jetty.util.LazyList;
31  import org.eclipse.jetty.util.log.Log;
32  
33  /* ------------------------------------------------------------ */
34  /** ContextHandlerCollection.
35   * 
36   * This {@link org.eclipse.jetty.server.handler.HandlerCollection} is creates a 
37   * {@link org.eclipse.jetty.http.servlet.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 volatile PathMap _contextMap;
48      private Class<? extends ContextHandler> _contextClass = ContextHandler.class;
49      
50      public ContextHandlerCollection()
51      {
52          super(true);
53      }
54      
55      /* ------------------------------------------------------------ */
56      /**
57       * Remap the context paths.
58       */
59      public void mapContexts()
60      {
61          PathMap contextMap = new PathMap();
62          Handler[] branches = getHandlers();
63          
64          
65          for (int b=0;branches!=null && b<branches.length;b++)
66          {
67              Handler[] handlers=null;
68              
69              if (branches[b] instanceof ContextHandler)
70              {
71                  handlers = new Handler[]{ branches[b] };
72              }
73              else if (branches[b] instanceof HandlerContainer)
74              {
75                  handlers = ((HandlerContainer)branches[b]).getChildHandlersByClass(ContextHandler.class);
76              }
77              else 
78                  continue;
79              
80              for (int i=0;i<handlers.length;i++)
81              {
82                  ContextHandler handler=(ContextHandler)handlers[i];
83  
84                  String contextPath=handler.getContextPath();
85  
86                  if (contextPath==null || contextPath.indexOf(',')>=0 || contextPath.startsWith("*"))
87                      throw new IllegalArgumentException ("Illegal context spec:"+contextPath);
88  
89                  if(!contextPath.startsWith("/"))
90                      contextPath='/'+contextPath;
91  
92                  if (contextPath.length()>1)
93                  {
94                      if (contextPath.endsWith("/"))
95                          contextPath+="*";
96                      else if (!contextPath.endsWith("/*"))
97                          contextPath+="/*";
98                  }
99  
100                 Object contexts=contextMap.get(contextPath);
101                 String[] vhosts=handler.getVirtualHosts();
102 
103                 
104                 if (vhosts!=null && vhosts.length>0)
105                 {
106                     Map hosts;
107 
108                     if (contexts instanceof Map)
109                         hosts=(Map)contexts;
110                     else
111                     {
112                         hosts=new HashMap(); 
113                         hosts.put("*",contexts);
114                         contextMap.put(contextPath, hosts);
115                     }
116 
117                     for (int j=0;j<vhosts.length;j++)
118                     {
119                         String vhost=vhosts[j];
120                         contexts=hosts.get(vhost);
121                         contexts=LazyList.add(contexts,branches[b]);
122                         hosts.put(vhost,contexts);
123                     }
124                 }
125                 else if (contexts instanceof Map)
126                 {
127                     Map hosts=(Map)contexts;
128                     contexts=hosts.get("*");
129                     contexts= LazyList.add(contexts, branches[b]);
130                     hosts.put("*",contexts);
131                 }
132                 else
133                 {
134                     contexts= LazyList.add(contexts, branches[b]);
135                     contextMap.put(contextPath, contexts);
136                 }
137             }
138         }
139         _contextMap=contextMap;
140 
141     }
142     
143 
144     
145     /* ------------------------------------------------------------ */
146     /* 
147      * @see org.eclipse.jetty.server.server.handler.HandlerCollection#setHandlers(org.eclipse.jetty.server.server.Handler[])
148      */
149     public void setHandlers(Handler[] handlers)
150     {
151         _contextMap=null;
152         super.setHandlers(handlers);
153         if (isStarted())
154             mapContexts();
155     }
156 
157     /* ------------------------------------------------------------ */
158     protected void doStart() throws Exception
159     {
160         mapContexts();
161         super.doStart();
162     }
163     
164 
165     /* ------------------------------------------------------------ */
166     /* 
167      * @see org.eclipse.jetty.server.server.Handler#handle(java.lang.String, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, int)
168      */
169     public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
170     {
171         Handler[] handlers = getHandlers();
172         if (handlers==null || handlers.length==0)
173 	    return;
174 	
175 	AsyncContinuation async = baseRequest.getAsyncContinuation();
176 	if (async.isAsync())
177 	{
178 	    ContextHandler context=async.getContextHandler();
179 	    if (context!=null)
180 	    {
181 	        context.handle(target,baseRequest,request, response);
182 	        return;
183 	    }
184 	}
185 	
186 	// data structure which maps a request to a context; first-best match wins
187 	// { context path => 
188 	//     { virtual host => context } 
189 	// }
190 	PathMap map = _contextMap;
191 	if (map!=null && target!=null && target.startsWith("/"))
192 	{
193 	    // first, get all contexts matched by context path
194 	    Object contexts = map.getLazyMatches(target);
195 
196             for (int i=0; i<LazyList.size(contexts); i++)
197             {
198                 // then, match against the virtualhost of each context
199                 Map.Entry entry = (Map.Entry)LazyList.get(contexts, i);
200                 Object list = entry.getValue();
201 
202                 if (list instanceof Map)
203                 {
204                     Map hosts = (Map)list;
205                     String host = normalizeHostname(request.getServerName());
206            
207                     // explicitly-defined virtual hosts, most specific
208                     list=hosts.get(host);
209                     for (int j=0; j<LazyList.size(list); j++)
210                     {
211                         Handler handler = (Handler)LazyList.get(list,j);
212                         handler.handle(target,baseRequest, request, response);
213                         if (baseRequest.isHandled())
214                             return;
215                     }
216                     
217                     // wildcard for one level of names 
218                     list=hosts.get("*."+host.substring(host.indexOf(".")+1));
219                     for (int j=0; j<LazyList.size(list); j++)
220                     {
221                         Handler handler = (Handler)LazyList.get(list,j);
222                         handler.handle(target,baseRequest, request, response);
223                         if (baseRequest.isHandled())
224                             return;
225                     }
226                     
227                     // no virtualhosts defined for the context, least specific
228                     // will handle any request that does not match to a specific virtual host above
229                     list=hosts.get("*");
230                     for (int j=0; j<LazyList.size(list); j++)
231                     {
232                         Handler handler = (Handler)LazyList.get(list,j);
233                         handler.handle(target,baseRequest, request, response);
234                         if (baseRequest.isHandled())
235                             return;
236                     }
237                 }
238                 else
239                 {
240                     for (int j=0; j<LazyList.size(list); j++)
241                     {
242                         Handler handler = (Handler)LazyList.get(list,j);
243                         handler.handle(target,baseRequest, request, response);
244                         if (baseRequest.isHandled())
245                             return;
246                     }
247                 }
248 	    }
249 	}
250 	else
251 	{
252             // This may not work in all circumstances... but then I think it should never be called
253 	    for (int i=0;i<handlers.length;i++)
254 	    {
255 		handlers[i].handle(target,baseRequest, request, response);
256 		if ( baseRequest.isHandled())
257 		    return;
258 	    }
259 	}
260     }
261     
262     
263     /* ------------------------------------------------------------ */
264     /** Add a context handler.
265      * @param contextPath  The context path to add
266      * @return
267      * @throws IllegalAccessException 
268      * @throws InstantiationException 
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.warn(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 }