1 // ======================================================================== 2 // Copyright 199-2004 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.servlet; 16 17 import java.io.IOException; 18 import java.util.Collections; 19 import java.util.Enumeration; 20 import java.util.HashSet; 21 import java.util.Iterator; 22 import java.util.Map; 23 24 import javax.servlet.RequestDispatcher; 25 import javax.servlet.ServletException; 26 import javax.servlet.ServletRequest; 27 import javax.servlet.ServletResponse; 28 import javax.servlet.http.HttpServletRequest; 29 import javax.servlet.http.HttpServletResponse; 30 31 import org.mortbay.jetty.Handler; 32 import org.mortbay.jetty.HttpConnection; 33 import org.mortbay.jetty.Request; 34 import org.mortbay.jetty.handler.ContextHandler; 35 import org.mortbay.util.Attributes; 36 import org.mortbay.util.LazyList; 37 import org.mortbay.util.MultiMap; 38 import org.mortbay.util.UrlEncoded; 39 40 /* ------------------------------------------------------------ */ 41 /** Servlet RequestDispatcher. 42 * 43 * @author Greg Wilkins (gregw) 44 */ 45 public class Dispatcher implements RequestDispatcher 46 { 47 /** Dispatch include attribute names */ 48 public final static String __INCLUDE_JETTY="org.mortbay.jetty.included"; 49 public final static String __INCLUDE_PREFIX="javax.servlet.include."; 50 public final static String __INCLUDE_REQUEST_URI= "javax.servlet.include.request_uri"; 51 public final static String __INCLUDE_CONTEXT_PATH= "javax.servlet.include.context_path"; 52 public final static String __INCLUDE_SERVLET_PATH= "javax.servlet.include.servlet_path"; 53 public final static String __INCLUDE_PATH_INFO= "javax.servlet.include.path_info"; 54 public final static String __INCLUDE_QUERY_STRING= "javax.servlet.include.query_string"; 55 56 /** Dispatch include attribute names */ 57 public final static String __FORWARD_JETTY="org.mortbay.jetty.forwarded"; 58 public final static String __FORWARD_PREFIX="javax.servlet.forward."; 59 public final static String __FORWARD_REQUEST_URI= "javax.servlet.forward.request_uri"; 60 public final static String __FORWARD_CONTEXT_PATH= "javax.servlet.forward.context_path"; 61 public final static String __FORWARD_SERVLET_PATH= "javax.servlet.forward.servlet_path"; 62 public final static String __FORWARD_PATH_INFO= "javax.servlet.forward.path_info"; 63 public final static String __FORWARD_QUERY_STRING= "javax.servlet.forward.query_string"; 64 65 /** JSP attributes */ 66 public final static String __JSP_FILE="org.apache.catalina.jsp_file"; 67 68 /* ------------------------------------------------------------ */ 69 /** Dispatch type from name 70 */ type(String type)71 public static int type(String type) 72 { 73 if ("request".equalsIgnoreCase(type)) 74 return Handler.REQUEST; 75 if ("forward".equalsIgnoreCase(type)) 76 return Handler.FORWARD; 77 if ("include".equalsIgnoreCase(type)) 78 return Handler.INCLUDE; 79 if ("error".equalsIgnoreCase(type)) 80 return Handler.ERROR; 81 throw new IllegalArgumentException(type); 82 } 83 84 85 /* ------------------------------------------------------------ */ 86 private ContextHandler _contextHandler; 87 private String _uri; 88 private String _path; 89 private String _dQuery; 90 private String _named; 91 92 /* ------------------------------------------------------------ */ 93 /** 94 * @param contextHandler 95 * @param uriInContext 96 * @param pathInContext 97 * @param query 98 */ Dispatcher(ContextHandler contextHandler, String uri, String pathInContext, String query)99 public Dispatcher(ContextHandler contextHandler, String uri, String pathInContext, String query) 100 { 101 _contextHandler=contextHandler; 102 _uri=uri; 103 _path=pathInContext; 104 _dQuery=query; 105 } 106 107 108 /* ------------------------------------------------------------ */ 109 /** Constructor. 110 * @param servletHandler 111 * @param name 112 */ Dispatcher(ContextHandler contextHandler,String name)113 public Dispatcher(ContextHandler contextHandler,String name) 114 throws IllegalStateException 115 { 116 _contextHandler=contextHandler; 117 _named=name; 118 } 119 120 /* ------------------------------------------------------------ */ 121 /* 122 * @see javax.servlet.RequestDispatcher#forward(javax.servlet.ServletRequest, javax.servlet.ServletResponse) 123 */ forward(ServletRequest request, ServletResponse response)124 public void forward(ServletRequest request, ServletResponse response) throws ServletException, IOException 125 { 126 forward(request, response, Handler.FORWARD); 127 } 128 129 /* ------------------------------------------------------------ */ 130 /* 131 * @see javax.servlet.RequestDispatcher#forward(javax.servlet.ServletRequest, javax.servlet.ServletResponse) 132 */ error(ServletRequest request, ServletResponse response)133 public void error(ServletRequest request, ServletResponse response) throws ServletException, IOException 134 { 135 forward(request, response, Handler.ERROR); 136 } 137 138 /* ------------------------------------------------------------ */ 139 /* 140 * @see javax.servlet.RequestDispatcher#include(javax.servlet.ServletRequest, javax.servlet.ServletResponse) 141 */ include(ServletRequest request, ServletResponse response)142 public void include(ServletRequest request, ServletResponse response) throws ServletException, IOException 143 { 144 Request base_request=(request instanceof Request)?((Request)request):HttpConnection.getCurrentConnection().getRequest(); 145 request.removeAttribute(__JSP_FILE); // TODO remove when glassfish 1044 is fixed 146 147 // TODO - allow stream or writer???? 148 149 Attributes old_attr=base_request.getAttributes(); 150 MultiMap old_params=base_request.getParameters(); 151 try 152 { 153 base_request.getConnection().include(); 154 if (_named!=null) 155 _contextHandler.handle(_named, (HttpServletRequest)request, (HttpServletResponse)response, Handler.INCLUDE); 156 else 157 { 158 String query=_dQuery; 159 160 if (query!=null) 161 { 162 MultiMap parameters=new MultiMap(); 163 UrlEncoded.decodeTo(query,parameters,request.getCharacterEncoding()); 164 165 if (old_params!=null && old_params.size()>0) 166 { 167 // Merge parameters. 168 Iterator iter = old_params.entrySet().iterator(); 169 while (iter.hasNext()) 170 { 171 Map.Entry entry = (Map.Entry)iter.next(); 172 String name=(String)entry.getKey(); 173 Object values=entry.getValue(); 174 for (int i=0;i<LazyList.size(values);i++) 175 parameters.add(name, LazyList.get(values, i)); 176 } 177 178 } 179 base_request.setParameters(parameters); 180 } 181 182 IncludeAttributes attr = new IncludeAttributes(old_attr); 183 184 attr._requestURI=_uri; 185 attr._contextPath=_contextHandler.getContextPath(); 186 attr._servletPath=null; // set by ServletHandler 187 attr._pathInfo=_path; 188 attr._query=query; 189 190 base_request.setAttributes(attr); 191 192 _contextHandler.handle(_named==null?_path:_named, (HttpServletRequest)request, (HttpServletResponse)response, Handler.INCLUDE); 193 } 194 } 195 finally 196 { 197 base_request.setAttributes(old_attr); 198 base_request.getConnection().included(); 199 base_request.setParameters(old_params); 200 } 201 } 202 203 204 /* ------------------------------------------------------------ */ 205 /* 206 * @see javax.servlet.RequestDispatcher#forward(javax.servlet.ServletRequest, javax.servlet.ServletResponse) 207 */ forward(ServletRequest request, ServletResponse response, int dispatch)208 protected void forward(ServletRequest request, ServletResponse response, int dispatch) throws ServletException, IOException 209 { 210 Request base_request=(request instanceof Request)?((Request)request):HttpConnection.getCurrentConnection().getRequest(); 211 response.resetBuffer(); 212 request.removeAttribute(__JSP_FILE); // TODO remove when glassfish 1044 is fixed 213 214 String old_uri=base_request.getRequestURI(); 215 String old_context_path=base_request.getContextPath(); 216 String old_servlet_path=base_request.getServletPath(); 217 String old_path_info=base_request.getPathInfo(); 218 String old_query=base_request.getQueryString(); 219 Attributes old_attr=base_request.getAttributes(); 220 MultiMap old_params=base_request.getParameters(); 221 try 222 { 223 if (_named!=null) 224 _contextHandler.handle(_named, (HttpServletRequest)request, (HttpServletResponse)response, dispatch); 225 else 226 { 227 String query=_dQuery; 228 229 if (query!=null) 230 { 231 MultiMap parameters=new MultiMap(); 232 UrlEncoded.decodeTo(query,parameters,request.getCharacterEncoding()); 233 234 boolean rewrite_old_query = false; 235 236 if( old_params == null ) 237 { 238 base_request.getParameterNames(); // force parameters to be evaluated 239 old_params = base_request.getParameters(); 240 } 241 242 if (old_params!=null && old_params.size()>0) 243 { 244 // Merge parameters; new parameters of the same name take precedence. 245 Iterator iter = old_params.entrySet().iterator(); 246 while (iter.hasNext()) 247 { 248 Map.Entry entry = (Map.Entry)iter.next(); 249 String name=(String)entry.getKey(); 250 251 if (parameters.containsKey(name)) 252 { 253 rewrite_old_query = true; 254 } 255 else 256 { 257 Object values=entry.getValue(); 258 for (int i=0;i<LazyList.size(values);i++) 259 { 260 parameters.add(name, LazyList.get(values, i)); 261 } 262 } 263 } 264 } 265 266 if (old_query != null && old_query.length()>0) 267 { 268 if ( rewrite_old_query ) 269 { 270 StringBuffer overridden_query_string = new StringBuffer(); 271 MultiMap overridden_old_query = new MultiMap(); 272 UrlEncoded.decodeTo(old_query,overridden_old_query,request.getCharacterEncoding()); 273 274 MultiMap overridden_new_query = new MultiMap(); 275 UrlEncoded.decodeTo(query,overridden_new_query,request.getCharacterEncoding()); 276 277 Iterator iter = overridden_old_query.entrySet().iterator(); 278 while (iter.hasNext()) 279 { 280 Map.Entry entry = (Map.Entry)iter.next(); 281 String name=(String)entry.getKey(); 282 if(!overridden_new_query.containsKey(name)) 283 { 284 Object values=entry.getValue(); 285 for (int i=0;i<LazyList.size(values);i++) 286 { 287 overridden_query_string.append("&"+name+"="+LazyList.get(values, i)); 288 } 289 } 290 } 291 292 query = query + overridden_query_string; 293 } 294 else 295 { 296 query=query+"&"+old_query; 297 } 298 } 299 300 base_request.setParameters(parameters); 301 base_request.setQueryString(query); 302 } 303 304 ForwardAttributes attr = new ForwardAttributes(old_attr); 305 306 //If we have already been forwarded previously, then keep using the established 307 //original value. Otherwise, this is the first forward and we need to establish the values. 308 //Note: the established value on the original request for pathInfo and 309 //for queryString is allowed to be null, but cannot be null for the other values. 310 if ((String)old_attr.getAttribute(__FORWARD_REQUEST_URI) != null) 311 { 312 attr._pathInfo=(String)old_attr.getAttribute(__FORWARD_PATH_INFO); 313 attr._query=(String)old_attr.getAttribute(__FORWARD_QUERY_STRING); 314 attr._requestURI=(String)old_attr.getAttribute(__FORWARD_REQUEST_URI); 315 attr._contextPath=(String)old_attr.getAttribute(__FORWARD_CONTEXT_PATH); 316 attr._servletPath=(String)old_attr.getAttribute(__FORWARD_SERVLET_PATH); 317 } 318 else 319 { 320 attr._pathInfo=old_path_info; 321 attr._query=old_query; 322 attr._requestURI=old_uri; 323 attr._contextPath=old_context_path; 324 attr._servletPath=old_servlet_path; 325 } 326 327 328 329 base_request.setRequestURI(_uri); 330 base_request.setContextPath(_contextHandler.getContextPath()); 331 base_request.setAttributes(attr); 332 base_request.setQueryString(query); 333 334 _contextHandler.handle(_path, (HttpServletRequest)request, (HttpServletResponse)response, dispatch); 335 336 if (base_request.getConnection().getResponse().isWriting()) 337 { 338 try {response.getWriter().close();} 339 catch(IllegalStateException e) { response.getOutputStream().close(); } 340 } 341 else 342 { 343 try {response.getOutputStream().close();} 344 catch(IllegalStateException e) { response.getWriter().close(); } 345 } 346 } 347 } 348 finally 349 { 350 base_request.setRequestURI(old_uri); 351 base_request.setContextPath(old_context_path); 352 base_request.setServletPath(old_servlet_path); 353 base_request.setPathInfo(old_path_info); 354 base_request.setAttributes(old_attr); 355 base_request.setParameters(old_params); 356 base_request.setQueryString(old_query); 357 } 358 } 359 360 361 /* ------------------------------------------------------------ */ 362 /* ------------------------------------------------------------ */ 363 /* ------------------------------------------------------------ */ 364 private class ForwardAttributes implements Attributes 365 { 366 Attributes _attr; 367 368 String _requestURI; 369 String _contextPath; 370 String _servletPath; 371 String _pathInfo; 372 String _query; 373 ForwardAttributes(Attributes attributes)374 ForwardAttributes(Attributes attributes) 375 { 376 _attr=attributes; 377 } 378 379 /* ------------------------------------------------------------ */ getAttribute(String key)380 public Object getAttribute(String key) 381 { 382 if (Dispatcher.this._named==null) 383 { 384 if (key.equals(__FORWARD_PATH_INFO)) return _pathInfo; 385 if (key.equals(__FORWARD_REQUEST_URI)) return _requestURI; 386 if (key.equals(__FORWARD_SERVLET_PATH)) return _servletPath; 387 if (key.equals(__FORWARD_CONTEXT_PATH)) return _contextPath; 388 if (key.equals(__FORWARD_QUERY_STRING)) return _query; 389 } 390 391 if (key.startsWith(__INCLUDE_PREFIX) || key.equals(__INCLUDE_JETTY) ) 392 return null; 393 394 if (key.equals(__FORWARD_JETTY)) 395 return Boolean.TRUE; 396 397 return _attr.getAttribute(key); 398 } 399 400 /* ------------------------------------------------------------ */ getAttributeNames()401 public Enumeration getAttributeNames() 402 { 403 HashSet set=new HashSet(); 404 Enumeration e=_attr.getAttributeNames(); 405 while(e.hasMoreElements()) 406 { 407 String name=(String)e.nextElement(); 408 if (!name.startsWith(__INCLUDE_PREFIX) && 409 !name.startsWith(__FORWARD_PREFIX)) 410 set.add(name); 411 } 412 413 if (_named==null) 414 { 415 if (_pathInfo!=null) 416 set.add(__FORWARD_PATH_INFO); 417 else 418 set.remove(__FORWARD_PATH_INFO); 419 set.add(__FORWARD_REQUEST_URI); 420 set.add(__FORWARD_SERVLET_PATH); 421 set.add(__FORWARD_CONTEXT_PATH); 422 if (_query!=null) 423 set.add(__FORWARD_QUERY_STRING); 424 else 425 set.remove(__FORWARD_QUERY_STRING); 426 } 427 428 return Collections.enumeration(set); 429 } 430 431 /* ------------------------------------------------------------ */ setAttribute(String key, Object value)432 public void setAttribute(String key, Object value) 433 { 434 if (_named==null && key.startsWith("javax.servlet.")) 435 { 436 if (key.equals(__FORWARD_PATH_INFO)) _pathInfo=(String)value; 437 else if (key.equals(__FORWARD_REQUEST_URI)) _requestURI=(String)value; 438 else if (key.equals(__FORWARD_SERVLET_PATH)) _servletPath=(String)value; 439 else if (key.equals(__FORWARD_CONTEXT_PATH)) _contextPath=(String)value; 440 else if (key.equals(__FORWARD_QUERY_STRING)) _query=(String)value; 441 442 else if (value==null) 443 _attr.removeAttribute(key); 444 else 445 _attr.setAttribute(key,value); 446 } 447 else if (value==null) 448 _attr.removeAttribute(key); 449 else 450 _attr.setAttribute(key,value); 451 } 452 453 /* ------------------------------------------------------------ */ toString()454 public String toString() 455 { 456 return "FORWARD+"+_attr.toString(); 457 } 458 459 /* ------------------------------------------------------------ */ clearAttributes()460 public void clearAttributes() 461 { 462 throw new IllegalStateException(); 463 } 464 465 /* ------------------------------------------------------------ */ removeAttribute(String name)466 public void removeAttribute(String name) 467 { 468 setAttribute(name,null); 469 } 470 } 471 472 /* ------------------------------------------------------------ */ 473 private class IncludeAttributes implements Attributes 474 { 475 Attributes _attr; 476 477 String _requestURI; 478 String _contextPath; 479 String _servletPath; 480 String _pathInfo; 481 String _query; 482 IncludeAttributes(Attributes attributes)483 IncludeAttributes(Attributes attributes) 484 { 485 _attr=attributes; 486 } 487 488 /* ------------------------------------------------------------ */ 489 /* ------------------------------------------------------------ */ 490 /* ------------------------------------------------------------ */ getAttribute(String key)491 public Object getAttribute(String key) 492 { 493 if (Dispatcher.this._named==null) 494 { 495 if (key.equals(__INCLUDE_PATH_INFO)) return _pathInfo; 496 if (key.equals(__INCLUDE_SERVLET_PATH)) return _servletPath; 497 if (key.equals(__INCLUDE_CONTEXT_PATH)) return _contextPath; 498 if (key.equals(__INCLUDE_QUERY_STRING)) return _query; 499 if (key.equals(__INCLUDE_REQUEST_URI)) return _requestURI; 500 } 501 else if (key.startsWith(__INCLUDE_PREFIX)) 502 return null; 503 504 if (key.equals(__INCLUDE_JETTY)) 505 return Boolean.TRUE; 506 507 return _attr.getAttribute(key); 508 } 509 510 /* ------------------------------------------------------------ */ getAttributeNames()511 public Enumeration getAttributeNames() 512 { 513 HashSet set=new HashSet(); 514 Enumeration e=_attr.getAttributeNames(); 515 while(e.hasMoreElements()) 516 { 517 String name=(String)e.nextElement(); 518 if (!name.startsWith(__INCLUDE_PREFIX)) 519 set.add(name); 520 } 521 522 if (_named==null) 523 { 524 if (_pathInfo!=null) 525 set.add(__INCLUDE_PATH_INFO); 526 else 527 set.remove(__INCLUDE_PATH_INFO); 528 set.add(__INCLUDE_REQUEST_URI); 529 set.add(__INCLUDE_SERVLET_PATH); 530 set.add(__INCLUDE_CONTEXT_PATH); 531 if (_query!=null) 532 set.add(__INCLUDE_QUERY_STRING); 533 else 534 set.remove(__INCLUDE_QUERY_STRING); 535 } 536 537 return Collections.enumeration(set); 538 } 539 540 /* ------------------------------------------------------------ */ setAttribute(String key, Object value)541 public void setAttribute(String key, Object value) 542 { 543 if (_named==null && key.startsWith("javax.servlet.")) 544 { 545 if (key.equals(__INCLUDE_PATH_INFO)) _pathInfo=(String)value; 546 else if (key.equals(__INCLUDE_REQUEST_URI)) _requestURI=(String)value; 547 else if (key.equals(__INCLUDE_SERVLET_PATH)) _servletPath=(String)value; 548 else if (key.equals(__INCLUDE_CONTEXT_PATH)) _contextPath=(String)value; 549 else if (key.equals(__INCLUDE_QUERY_STRING)) _query=(String)value; 550 else if (value==null) 551 _attr.removeAttribute(key); 552 else 553 _attr.setAttribute(key,value); 554 } 555 else if (value==null) 556 _attr.removeAttribute(key); 557 else 558 _attr.setAttribute(key,value); 559 } 560 561 /* ------------------------------------------------------------ */ toString()562 public String toString() 563 { 564 return "INCLUDE+"+_attr.toString(); 565 } 566 567 /* ------------------------------------------------------------ */ clearAttributes()568 public void clearAttributes() 569 { 570 throw new IllegalStateException(); 571 } 572 573 /* ------------------------------------------------------------ */ removeAttribute(String name)574 public void removeAttribute(String name) 575 { 576 setAttribute(name,null); 577 } 578 } 579 }; 580