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