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.warn(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 }