View Javadoc

1   //========================================================================
2   //Copyright 2006 Mort Bay Consulting Pty. Ltd.
3   //------------------------------------------------------------------------
4   //Licensed under the Apache License, Version 2.0 (the "License");
5   //you may not use this file except in compliance with the License.
6   //You may obtain a copy of the License at 
7   //http://www.apache.org/licenses/LICENSE-2.0
8   //Unless required by applicable law or agreed to in writing, software
9   //distributed under the License is distributed on an "AS IS" BASIS,
10  //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11  //See the License for the specific language governing permissions and
12  //limitations under the License.
13  //========================================================================
14  
15  package org.mortbay.jetty.handler;
16  
17  import java.io.IOException;
18  import java.util.HashMap;
19  import java.util.Map;
20  
21  import javax.servlet.ServletException;
22  import javax.servlet.http.HttpServletRequest;
23  import javax.servlet.http.HttpServletResponse;
24  
25  import org.mortbay.jetty.Handler;
26  import org.mortbay.jetty.HandlerContainer;
27  import org.mortbay.jetty.HttpConnection;
28  import org.mortbay.jetty.Request;
29  import org.mortbay.jetty.servlet.PathMap;
30  import org.mortbay.log.Log;
31  import org.mortbay.util.LazyList;
32  
33  /* ------------------------------------------------------------ */
34  /** ContextHandlerCollection.
35   * 
36   * This {@link org.mortbay.jetty.handler.HandlerCollection} is creates a 
37   * {@link org.mortbay.jetty.servlet.PathMap} to it's contained handlers based
38   * on the context path and virtual hosts of any contained {@link org.mortbay.jetty.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 PathMap _contextMap;
48      private Class _contextClass = ContextHandler.class;
49      
50      /* ------------------------------------------------------------ */
51      /**
52       * Remap the context paths.
53       */
54      public void mapContexts()
55      {
56          PathMap contextMap = new PathMap();
57          Handler[] branches = getHandlers();
58          
59          
60          for (int b=0;branches!=null && b<branches.length;b++)
61          {
62              Handler[] handlers=null;
63              
64              if (branches[b] instanceof ContextHandler)
65              {
66                  handlers = new Handler[]{ branches[b] };
67              }
68              else if (branches[b] instanceof HandlerContainer)
69              {
70                  handlers = ((HandlerContainer)branches[b]).getChildHandlersByClass(ContextHandler.class);
71              }
72              else 
73                  continue;
74              
75              for (int i=0;i<handlers.length;i++)
76              {
77                  ContextHandler handler=(ContextHandler)handlers[i];
78  
79                  String contextPath=handler.getContextPath();
80  
81                  if (contextPath==null || contextPath.indexOf(',')>=0 || contextPath.startsWith("*"))
82                      throw new IllegalArgumentException ("Illegal context spec:"+contextPath);
83  
84                  if(!contextPath.startsWith("/"))
85                      contextPath='/'+contextPath;
86  
87                  if (contextPath.length()>1)
88                  {
89                      if (contextPath.endsWith("/"))
90                          contextPath+="*";
91                      else if (!contextPath.endsWith("/*"))
92                          contextPath+="/*";
93                  }
94  
95                  Object contexts=contextMap.get(contextPath);
96                  String[] vhosts=handler.getVirtualHosts();
97  
98                  
99                  if (vhosts!=null && vhosts.length>0)
100                 {
101                     Map hosts;
102 
103                     if (contexts instanceof Map)
104                         hosts=(Map)contexts;
105                     else
106                     {
107                         hosts=new HashMap(); 
108                         hosts.put("*",contexts);
109                         contextMap.put(contextPath, hosts);
110                     }
111 
112                     for (int j=0;j<vhosts.length;j++)
113                     {
114                         String vhost=vhosts[j];
115                         contexts=hosts.get(vhost);
116                         contexts=LazyList.add(contexts,branches[b]);
117                         hosts.put(vhost,contexts);
118                     }
119                 }
120                 else if (contexts instanceof Map)
121                 {
122                     Map hosts=(Map)contexts;
123                     contexts=hosts.get("*");
124                     contexts= LazyList.add(contexts, branches[b]);
125                     hosts.put("*",contexts);
126                 }
127                 else
128                 {
129                     contexts= LazyList.add(contexts, branches[b]);
130                     contextMap.put(contextPath, contexts);
131                 }
132             }
133         }
134         _contextMap=contextMap;
135 
136     }
137     
138 
139     
140     /* ------------------------------------------------------------ */
141     /* 
142      * @see org.mortbay.jetty.handler.HandlerCollection#setHandlers(org.mortbay.jetty.Handler[])
143      */
144     public void setHandlers(Handler[] handlers)
145     {
146         _contextMap=null;
147         super.setHandlers(handlers);
148         if (isStarted())
149             mapContexts();
150     }
151 
152     /* ------------------------------------------------------------ */
153     protected void doStart() throws Exception
154     {
155         mapContexts();
156         super.doStart();
157     }
158     
159 
160     /* ------------------------------------------------------------ */
161     /* 
162      * @see org.mortbay.jetty.Handler#handle(java.lang.String, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, int)
163      */
164     public void handle(String target, HttpServletRequest request, HttpServletResponse response, int dispatch) throws IOException, ServletException
165     {
166         Handler[] handlers = getHandlers();
167         if (handlers==null || handlers.length==0)
168 	    return;
169 
170         Request base_request = HttpConnection.getCurrentConnection().getRequest();
171       
172         // data structure which maps a request to a context
173         // each match is called in turn until the request is handled
174         // { context path => 
175         //     { virtual host => context } 
176         // }
177 	PathMap map = _contextMap;
178 	if (map!=null && target!=null && target.startsWith("/"))
179 	{
180             // first, get all contexts matched by context path
181 	    Object contexts = map.getLazyMatches(target);
182 
183             for (int i=0; i<LazyList.size(contexts); i++)
184             {
185                 // then, match against the virtualhost of each context
186                 Map.Entry entry = (Map.Entry)LazyList.get(contexts, i);
187                 Object list = entry.getValue();
188 
189                 if (list instanceof Map)
190                 {
191                     Map hosts = (Map)list;
192                     String host = normalizeHostname(request.getServerName());
193 
194                     // explicitly-defined virtual hosts, most specific
195                     list=hosts.get(host);
196                     for (int j=0; j<LazyList.size(list); j++)
197                     {
198                         Handler handler = (Handler)LazyList.get(list,j);
199                         handler.handle(target,request, response, dispatch);
200                         if (base_request.isHandled())
201                             return;
202                     }
203 
204                     // wildcard for one level of names 
205                     list=hosts.get("*."+host.substring(host.indexOf(".")+1));
206                     for (int j=0; j<LazyList.size(list); j++)
207                     {
208                         Handler handler = (Handler)LazyList.get(list,j);
209                         handler.handle(target,request, response, dispatch);
210                         if (base_request.isHandled())
211                             return;
212                     }
213 
214                     // no virtualhosts defined for the context, least specific
215                     // will handle any request that does not match to a specific virtual host above                    
216                     list=hosts.get("*");
217                     for (int j=0; j<LazyList.size(list); j++)
218                     {
219                         Handler handler = (Handler)LazyList.get(list,j);
220                         handler.handle(target,request, response, dispatch);
221                         if (base_request.isHandled())
222                             return;
223                     }
224                 }
225                 else
226                 {
227                     for (int j=0; j<LazyList.size(list); j++)
228                     {
229                         Handler handler = (Handler)LazyList.get(list,j);
230                         handler.handle(target,request, response, dispatch);
231                         if (base_request.isHandled())
232                             return;
233                     }
234                 }
235 	    }
236 	}
237 	else
238 	{
239             // This may not work in all circumstances... but then I think it should never be called
240 	    for (int i=0;i<handlers.length;i++)
241 	    {
242 		handlers[i].handle(target,request, response, dispatch);
243 		if ( base_request.isHandled())
244 		    return;
245 	    }
246 	}
247     }
248     
249     
250     /* ------------------------------------------------------------ */
251     /** Add a context handler.
252      * @param contextPath  The context path to add
253      * @return
254      * @throws IllegalAccessException 
255      * @throws InstantiationException 
256      */
257     public ContextHandler addContext(String contextPath,String resourceBase) 
258     {
259         try
260         {
261             ContextHandler context = (ContextHandler)_contextClass.newInstance();
262             context.setContextPath(contextPath);
263             context.setResourceBase(resourceBase);
264             addHandler(context);
265             return context;
266         }
267         catch (Exception e)
268         {
269             Log.debug(e);
270             throw new Error(e);
271         }
272     }
273 
274 
275 
276     /* ------------------------------------------------------------ */
277     /**
278      * @return The class to use to add new Contexts
279      */
280     public Class getContextClass()
281     {
282         return _contextClass;
283     }
284 
285 
286     /* ------------------------------------------------------------ */
287     /**
288      * @param contextClass The class to use to add new Contexts
289      */
290     public void setContextClass(Class contextClass)
291     {
292         if (contextClass ==null || !(ContextHandler.class.isAssignableFrom(contextClass)))
293             throw new IllegalArgumentException();
294         _contextClass = contextClass;
295     }
296     
297     /* ------------------------------------------------------------ */
298     private String normalizeHostname( String host )
299     {
300         if ( host == null )
301             return null;
302         
303         if ( host.endsWith( "." ) )
304             return host.substring( 0, host.length() -1);
305       
306         return host;
307     }
308 
309 }