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