1 // 2 // ======================================================================== 3 // Copyright (c) 1995-2014 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; 20 21 import javax.servlet.AsyncContext; 22 import javax.servlet.AsyncEvent; 23 import javax.servlet.AsyncListener; 24 import javax.servlet.RequestDispatcher; 25 import javax.servlet.ServletException; 26 27 import java.util.ArrayList; 28 import java.util.List; 29 30 import javax.servlet.ServletContext; 31 import javax.servlet.ServletRequest; 32 import javax.servlet.ServletResponse; 33 import javax.servlet.http.HttpServletRequest; 34 35 import org.eclipse.jetty.continuation.Continuation; 36 import org.eclipse.jetty.continuation.ContinuationThrowable; 37 import org.eclipse.jetty.continuation.ContinuationListener; 38 import org.eclipse.jetty.io.AsyncEndPoint; 39 import org.eclipse.jetty.io.EndPoint; 40 import org.eclipse.jetty.server.handler.ContextHandler; 41 import org.eclipse.jetty.server.handler.ContextHandler.Context; 42 import org.eclipse.jetty.util.URIUtil; 43 import org.eclipse.jetty.util.log.Log; 44 import org.eclipse.jetty.util.log.Logger; 45 import org.eclipse.jetty.util.thread.Timeout; 46 47 /* ------------------------------------------------------------ */ 48 /** Implementation of Continuation and AsyncContext interfaces 49 * 50 */ 51 public class AsyncContinuation implements AsyncContext, Continuation 52 { 53 private static final Logger LOG = Log.getLogger(AsyncContinuation.class); 54 55 private final static long DEFAULT_TIMEOUT=30000L; 56 57 private final static ContinuationThrowable __exception = new ContinuationThrowable(); 58 59 // STATES: 60 // handling() suspend() unhandle() resume() complete() doComplete() 61 // startAsync() dispatch() 62 // IDLE DISPATCHED 63 // DISPATCHED ASYNCSTARTED UNCOMPLETED 64 // ASYNCSTARTED ASYNCWAIT REDISPATCHING COMPLETING 65 // REDISPATCHING REDISPATCHED 66 // ASYNCWAIT REDISPATCH COMPLETING 67 // REDISPATCH REDISPATCHED 68 // REDISPATCHED ASYNCSTARTED UNCOMPLETED 69 // COMPLETING UNCOMPLETED UNCOMPLETED 70 // UNCOMPLETED COMPLETED 71 // COMPLETED 72 private static final int __IDLE=0; // Idle request 73 private static final int __DISPATCHED=1; // Request dispatched to filter/servlet 74 private static final int __ASYNCSTARTED=2; // Suspend called, but not yet returned to container 75 private static final int __REDISPATCHING=3;// resumed while dispatched 76 private static final int __ASYNCWAIT=4; // Suspended and parked 77 private static final int __REDISPATCH=5; // Has been scheduled 78 private static final int __REDISPATCHED=6; // Request redispatched to filter/servlet 79 private static final int __COMPLETING=7; // complete while dispatched 80 private static final int __UNCOMPLETED=8; // Request is completable 81 private static final int __COMPLETED=9; // Request is complete 82 83 /* ------------------------------------------------------------ */ 84 protected AbstractHttpConnection _connection; 85 private List<AsyncListener> _lastAsyncListeners; 86 private List<AsyncListener> _asyncListeners; 87 private List<ContinuationListener> _continuationListeners; 88 89 /* ------------------------------------------------------------ */ 90 private int _state; 91 private boolean _initial; 92 private boolean _resumed; 93 private boolean _expired; 94 private volatile boolean _responseWrapped; 95 private long _timeoutMs=DEFAULT_TIMEOUT; 96 private AsyncEventState _event; 97 private volatile long _expireAt; 98 private volatile boolean _continuation; 99 100 /* ------------------------------------------------------------ */ 101 protected AsyncContinuation() 102 { 103 _state=__IDLE; 104 _initial=true; 105 } 106 107 /* ------------------------------------------------------------ */ 108 protected void setConnection(final AbstractHttpConnection connection) 109 { 110 synchronized(this) 111 { 112 _connection=connection; 113 } 114 } 115 116 /* ------------------------------------------------------------ */ 117 public void addListener(AsyncListener listener) 118 { 119 synchronized(this) 120 { 121 if (_asyncListeners==null) 122 _asyncListeners=new ArrayList<AsyncListener>(); 123 _asyncListeners.add(listener); 124 } 125 } 126 127 /* ------------------------------------------------------------ */ 128 public void addListener(AsyncListener listener,ServletRequest request, ServletResponse response) 129 { 130 synchronized(this) 131 { 132 // TODO handle the request/response ??? 133 if (_asyncListeners==null) 134 _asyncListeners=new ArrayList<AsyncListener>(); 135 _asyncListeners.add(listener); 136 } 137 } 138 139 /* ------------------------------------------------------------ */ 140 public void addContinuationListener(ContinuationListener listener) 141 { 142 synchronized(this) 143 { 144 if (_continuationListeners==null) 145 _continuationListeners=new ArrayList<ContinuationListener>(); 146 _continuationListeners.add(listener); 147 } 148 } 149 150 /* ------------------------------------------------------------ */ 151 public void setTimeout(long ms) 152 { 153 synchronized(this) 154 { 155 _timeoutMs=ms; 156 } 157 } 158 159 /* ------------------------------------------------------------ */ 160 public long getTimeout() 161 { 162 synchronized(this) 163 { 164 return _timeoutMs; 165 } 166 } 167 168 /* ------------------------------------------------------------ */ 169 public AsyncEventState getAsyncEventState() 170 { 171 synchronized(this) 172 { 173 return _event; 174 } 175 } 176 177 /* ------------------------------------------------------------ */ 178 /** 179 * @see org.eclipse.jetty.continuation.Continuation#keepWrappers() 180 */ 181 182 /* ------------------------------------------------------------ */ 183 /** 184 * @see org.eclipse.jetty.continuation.Continuation#isResponseWrapped() 185 */ 186 public boolean isResponseWrapped() 187 { 188 return _responseWrapped; 189 } 190 191 /* ------------------------------------------------------------ */ 192 /* (non-Javadoc) 193 * @see javax.servlet.ServletRequest#isInitial() 194 */ 195 public boolean isInitial() 196 { 197 synchronized(this) 198 { 199 return _initial; 200 } 201 } 202 203 public boolean isContinuation() 204 { 205 return _continuation; 206 } 207 208 /* ------------------------------------------------------------ */ 209 /* (non-Javadoc) 210 * @see javax.servlet.ServletRequest#isSuspended() 211 */ 212 public boolean isSuspended() 213 { 214 synchronized(this) 215 { 216 switch(_state) 217 { 218 case __ASYNCSTARTED: 219 case __REDISPATCHING: 220 case __COMPLETING: 221 case __ASYNCWAIT: 222 return true; 223 224 default: 225 return false; 226 } 227 } 228 } 229 230 /* ------------------------------------------------------------ */ 231 public boolean isSuspending() 232 { 233 synchronized(this) 234 { 235 switch(_state) 236 { 237 case __ASYNCSTARTED: 238 case __ASYNCWAIT: 239 return true; 240 241 default: 242 return false; 243 } 244 } 245 } 246 247 /* ------------------------------------------------------------ */ 248 public boolean isDispatchable() 249 { 250 synchronized(this) 251 { 252 switch(_state) 253 { 254 case __REDISPATCH: 255 case __REDISPATCHED: 256 case __REDISPATCHING: 257 case __COMPLETING: 258 return true; 259 260 default: 261 return false; 262 } 263 } 264 } 265 266 /* ------------------------------------------------------------ */ 267 @Override 268 public String toString() 269 { 270 synchronized (this) 271 { 272 return super.toString()+"@"+getStatusString(); 273 } 274 } 275 276 /* ------------------------------------------------------------ */ 277 public String getStatusString() 278 { 279 synchronized (this) 280 { 281 return 282 ((_state==__IDLE)?"IDLE": 283 (_state==__DISPATCHED)?"DISPATCHED": 284 (_state==__ASYNCSTARTED)?"ASYNCSTARTED": 285 (_state==__ASYNCWAIT)?"ASYNCWAIT": 286 (_state==__REDISPATCHING)?"REDISPATCHING": 287 (_state==__REDISPATCH)?"REDISPATCH": 288 (_state==__REDISPATCHED)?"REDISPATCHED": 289 (_state==__COMPLETING)?"COMPLETING": 290 (_state==__UNCOMPLETED)?"UNCOMPLETED": 291 (_state==__COMPLETED)?"COMPLETE": 292 ("UNKNOWN?"+_state))+ 293 (_initial?",initial":"")+ 294 (_resumed?",resumed":"")+ 295 (_expired?",expired":""); 296 } 297 } 298 299 /* ------------------------------------------------------------ */ 300 /** 301 * @return false if the handling of the request should not proceed 302 */ 303 protected boolean handling() 304 { 305 synchronized (this) 306 { 307 _continuation=false; 308 309 switch(_state) 310 { 311 case __IDLE: 312 _initial=true; 313 _state=__DISPATCHED; 314 if (_lastAsyncListeners!=null) 315 _lastAsyncListeners.clear(); 316 if (_asyncListeners!=null) 317 _asyncListeners.clear(); 318 else 319 { 320 _asyncListeners=_lastAsyncListeners; 321 _lastAsyncListeners=null; 322 } 323 return true; 324 325 case __COMPLETING: 326 _state=__UNCOMPLETED; 327 return false; 328 329 case __ASYNCWAIT: 330 return false; 331 332 case __REDISPATCH: 333 _state=__REDISPATCHED; 334 return true; 335 336 default: 337 throw new IllegalStateException(this.getStatusString()); 338 } 339 } 340 } 341 342 /* ------------------------------------------------------------ */ 343 /* (non-Javadoc) 344 * @see javax.servlet.ServletRequest#suspend(long) 345 */ 346 private void doSuspend(final ServletContext context, 347 final ServletRequest request, 348 final ServletResponse response) 349 { 350 synchronized (this) 351 { 352 switch(_state) 353 { 354 case __DISPATCHED: 355 case __REDISPATCHED: 356 _resumed=false; 357 _expired=false; 358 359 if (_event==null || request!=_event.getSuppliedRequest() || response != _event.getSuppliedResponse() || context != _event.getServletContext()) 360 _event=new AsyncEventState(context,request,response); 361 else 362 { 363 _event._dispatchContext=null; 364 _event._pathInContext=null; 365 } 366 _state=__ASYNCSTARTED; 367 List<AsyncListener> recycle=_lastAsyncListeners; 368 _lastAsyncListeners=_asyncListeners; 369 _asyncListeners=recycle; 370 if (_asyncListeners!=null) 371 _asyncListeners.clear(); 372 break; 373 374 default: 375 throw new IllegalStateException(this.getStatusString()); 376 } 377 } 378 379 if (_lastAsyncListeners!=null) 380 { 381 for (AsyncListener listener : _lastAsyncListeners) 382 { 383 try 384 { 385 listener.onStartAsync(_event); 386 } 387 catch(Exception e) 388 { 389 LOG.warn(e); 390 } 391 } 392 } 393 } 394 395 /* ------------------------------------------------------------ */ 396 /** 397 * Signal that the HttpConnection has finished handling the request. 398 * For blocking connectors, this call may block if the request has 399 * been suspended (startAsync called). 400 * @return true if handling is complete, false if the request should 401 * be handled again (eg because of a resume that happened before unhandle was called) 402 */ 403 protected boolean unhandle() 404 { 405 synchronized (this) 406 { 407 switch(_state) 408 { 409 case __REDISPATCHED: 410 case __DISPATCHED: 411 _state=__UNCOMPLETED; 412 return true; 413 414 case __IDLE: 415 throw new IllegalStateException(this.getStatusString()); 416 417 case __ASYNCSTARTED: 418 _initial=false; 419 _state=__ASYNCWAIT; 420 scheduleTimeout(); // could block and change state. 421 if (_state==__ASYNCWAIT) 422 return true; 423 else if (_state==__COMPLETING) 424 { 425 _state=__UNCOMPLETED; 426 return true; 427 } 428 _initial=false; 429 _state=__REDISPATCHED; 430 return false; 431 432 case __REDISPATCHING: 433 _initial=false; 434 _state=__REDISPATCHED; 435 return false; 436 437 case __COMPLETING: 438 _initial=false; 439 _state=__UNCOMPLETED; 440 return true; 441 442 default: 443 throw new IllegalStateException(this.getStatusString()); 444 } 445 } 446 } 447 448 /* ------------------------------------------------------------ */ 449 public void dispatch() 450 { 451 boolean dispatch=false; 452 synchronized (this) 453 { 454 switch(_state) 455 { 456 case __ASYNCSTARTED: 457 _state=__REDISPATCHING; 458 _resumed=true; 459 return; 460 461 case __ASYNCWAIT: 462 dispatch=!_expired; 463 _state=__REDISPATCH; 464 _resumed=true; 465 break; 466 467 case __REDISPATCH: 468 return; 469 470 default: 471 throw new IllegalStateException(this.getStatusString()); 472 } 473 } 474 475 if (dispatch) 476 { 477 cancelTimeout(); 478 scheduleDispatch(); 479 } 480 } 481 482 /* ------------------------------------------------------------ */ 483 protected void expired() 484 { 485 final List<ContinuationListener> cListeners; 486 final List<AsyncListener> aListeners; 487 synchronized (this) 488 { 489 switch(_state) 490 { 491 case __ASYNCSTARTED: 492 case __ASYNCWAIT: 493 cListeners=_continuationListeners; 494 aListeners=_asyncListeners; 495 break; 496 default: 497 cListeners=null; 498 aListeners=null; 499 return; 500 } 501 _expired=true; 502 } 503 504 if (aListeners!=null) 505 { 506 for (AsyncListener listener : aListeners) 507 { 508 try 509 { 510 listener.onTimeout(_event); 511 } 512 catch(Exception e) 513 { 514 LOG.debug(e); 515 _connection.getRequest().setAttribute(RequestDispatcher.ERROR_EXCEPTION,e); 516 break; 517 } 518 } 519 } 520 if (cListeners!=null) 521 { 522 for (ContinuationListener listener : cListeners) 523 { 524 try 525 { 526 listener.onTimeout(this); 527 } 528 catch(Exception e) 529 { 530 LOG.warn(e); 531 } 532 } 533 } 534 535 synchronized (this) 536 { 537 switch(_state) 538 { 539 case __ASYNCSTARTED: 540 case __ASYNCWAIT: 541 dispatch(); 542 break; 543 544 default: 545 if (!_continuation) 546 _expired=false; 547 } 548 } 549 550 scheduleDispatch(); 551 } 552 553 /* ------------------------------------------------------------ */ 554 /* (non-Javadoc) 555 * @see javax.servlet.ServletRequest#complete() 556 */ 557 public void complete() 558 { 559 // just like resume, except don't set _resumed=true; 560 boolean dispatch=false; 561 synchronized (this) 562 { 563 switch(_state) 564 { 565 case __DISPATCHED: 566 case __REDISPATCHED: 567 throw new IllegalStateException(this.getStatusString()); 568 569 case __ASYNCSTARTED: 570 _state=__COMPLETING; 571 return; 572 573 case __ASYNCWAIT: 574 _state=__COMPLETING; 575 dispatch=!_expired; 576 break; 577 578 default: 579 throw new IllegalStateException(this.getStatusString()); 580 } 581 } 582 583 if (dispatch) 584 { 585 cancelTimeout(); 586 scheduleDispatch(); 587 } 588 } 589 590 /* ------------------------------------------------------------ */ 591 /* (non-Javadoc) 592 * @see javax.servlet.ServletRequest#complete() 593 */ 594 public void errorComplete() 595 { 596 // just like complete except can overrule a prior dispatch call; 597 synchronized (this) 598 { 599 switch(_state) 600 { 601 case __REDISPATCHING: 602 case __ASYNCSTARTED: 603 _state=__COMPLETING; 604 _resumed=false; 605 return; 606 607 case __COMPLETING: 608 return; 609 610 default: 611 throw new IllegalStateException(this.getStatusString()); 612 } 613 } 614 } 615 616 /* ------------------------------------------------------------ */ 617 @Override 618 public <T extends AsyncListener> T createListener(Class<T> clazz) throws ServletException 619 { 620 try 621 { 622 // TODO inject 623 return clazz.newInstance(); 624 } 625 catch(Exception e) 626 { 627 throw new ServletException(e); 628 } 629 } 630 631 632 /* ------------------------------------------------------------ */ 633 /* (non-Javadoc) 634 * @see javax.servlet.ServletRequest#complete() 635 */ 636 protected void doComplete(Throwable ex) 637 { 638 final List<ContinuationListener> cListeners; 639 final List<AsyncListener> aListeners; 640 synchronized (this) 641 { 642 switch(_state) 643 { 644 case __UNCOMPLETED: 645 _state=__COMPLETED; 646 cListeners=_continuationListeners; 647 aListeners=_asyncListeners; 648 break; 649 650 default: 651 cListeners=null; 652 aListeners=null; 653 throw new IllegalStateException(this.getStatusString()); 654 } 655 } 656 657 if (aListeners!=null) 658 { 659 for (AsyncListener listener : aListeners) 660 { 661 try 662 { 663 if (ex!=null) 664 { 665 _event.getSuppliedRequest().setAttribute(RequestDispatcher.ERROR_EXCEPTION,ex); 666 _event.getSuppliedRequest().setAttribute(RequestDispatcher.ERROR_MESSAGE,ex.getMessage()); 667 listener.onError(_event); 668 } 669 else 670 listener.onComplete(_event); 671 } 672 catch(Exception e) 673 { 674 LOG.warn(e); 675 } 676 } 677 } 678 if (cListeners!=null) 679 { 680 for (ContinuationListener listener : cListeners) 681 { 682 try 683 { 684 listener.onComplete(this); 685 } 686 catch(Exception e) 687 { 688 LOG.warn(e); 689 } 690 } 691 } 692 } 693 694 /* ------------------------------------------------------------ */ 695 protected void recycle() 696 { 697 synchronized (this) 698 { 699 switch(_state) 700 { 701 case __DISPATCHED: 702 case __REDISPATCHED: 703 throw new IllegalStateException(getStatusString()); 704 default: 705 _state=__IDLE; 706 } 707 _initial = true; 708 _resumed=false; 709 _expired=false; 710 _responseWrapped=false; 711 cancelTimeout(); 712 _timeoutMs=DEFAULT_TIMEOUT; 713 _continuationListeners=null; 714 } 715 } 716 717 /* ------------------------------------------------------------ */ 718 public void cancel() 719 { 720 synchronized (this) 721 { 722 cancelTimeout(); 723 _continuationListeners=null; 724 } 725 } 726 727 /* ------------------------------------------------------------ */ 728 protected void scheduleDispatch() 729 { 730 EndPoint endp=_connection.getEndPoint(); 731 if (!endp.isBlocking()) 732 { 733 ((AsyncEndPoint)endp).asyncDispatch(); 734 } 735 } 736 737 /* ------------------------------------------------------------ */ 738 protected void scheduleTimeout() 739 { 740 EndPoint endp=_connection.getEndPoint(); 741 if (_timeoutMs>0) 742 { 743 if (endp.isBlocking()) 744 { 745 synchronized(this) 746 { 747 _expireAt = System.currentTimeMillis()+_timeoutMs; 748 long wait=_timeoutMs; 749 while (_expireAt>0 && wait>0 && _connection.getServer().isRunning()) 750 { 751 try 752 { 753 this.wait(wait); 754 } 755 catch (InterruptedException e) 756 { 757 LOG.ignore(e); 758 } 759 wait=_expireAt-System.currentTimeMillis(); 760 } 761 762 if (_expireAt>0 && wait<=0 && _connection.getServer().isRunning()) 763 { 764 expired(); 765 } 766 } 767 } 768 else 769 { 770 ((AsyncEndPoint)endp).scheduleTimeout(_event._timeout,_timeoutMs); 771 } 772 } 773 } 774 775 /* ------------------------------------------------------------ */ 776 protected void cancelTimeout() 777 { 778 EndPoint endp=_connection.getEndPoint(); 779 if (endp.isBlocking()) 780 { 781 synchronized(this) 782 { 783 _expireAt=0; 784 this.notifyAll(); 785 } 786 } 787 else 788 { 789 final AsyncEventState event=_event; 790 if (event!=null) 791 { 792 ((AsyncEndPoint)endp).cancelTimeout(event._timeout); 793 } 794 } 795 } 796 797 /* ------------------------------------------------------------ */ 798 public boolean isCompleting() 799 { 800 synchronized (this) 801 { 802 return _state==__COMPLETING; 803 } 804 } 805 806 /* ------------------------------------------------------------ */ 807 boolean isUncompleted() 808 { 809 synchronized (this) 810 { 811 return _state==__UNCOMPLETED; 812 } 813 } 814 815 /* ------------------------------------------------------------ */ 816 public boolean isComplete() 817 { 818 synchronized (this) 819 { 820 return _state==__COMPLETED; 821 } 822 } 823 824 825 /* ------------------------------------------------------------ */ 826 public boolean isAsyncStarted() 827 { 828 synchronized (this) 829 { 830 switch(_state) 831 { 832 case __ASYNCSTARTED: 833 case __REDISPATCHING: 834 case __REDISPATCH: 835 case __ASYNCWAIT: 836 return true; 837 838 default: 839 return false; 840 } 841 } 842 } 843 844 845 /* ------------------------------------------------------------ */ 846 public boolean isAsync() 847 { 848 synchronized (this) 849 { 850 switch(_state) 851 { 852 case __IDLE: 853 case __DISPATCHED: 854 case __UNCOMPLETED: 855 case __COMPLETED: 856 return false; 857 858 default: 859 return true; 860 } 861 } 862 } 863 864 /* ------------------------------------------------------------ */ 865 public void dispatch(ServletContext context, String path) 866 { 867 _event._dispatchContext=context; 868 _event.setPath(path); 869 dispatch(); 870 } 871 872 /* ------------------------------------------------------------ */ 873 public void dispatch(String path) 874 { 875 _event.setPath(path); 876 dispatch(); 877 } 878 879 /* ------------------------------------------------------------ */ 880 public Request getBaseRequest() 881 { 882 return _connection.getRequest(); 883 } 884 885 /* ------------------------------------------------------------ */ 886 public ServletRequest getRequest() 887 { 888 if (_event!=null) 889 return _event.getSuppliedRequest(); 890 return _connection.getRequest(); 891 } 892 893 /* ------------------------------------------------------------ */ 894 public ServletResponse getResponse() 895 { 896 if (_responseWrapped && _event!=null && _event.getSuppliedResponse()!=null) 897 return _event.getSuppliedResponse(); 898 return _connection.getResponse(); 899 } 900 901 /* ------------------------------------------------------------ */ 902 public void start(final Runnable run) 903 { 904 final AsyncEventState event=_event; 905 if (event!=null) 906 { 907 _connection.getServer().getThreadPool().dispatch(new Runnable() 908 { 909 public void run() 910 { 911 ((Context)event.getServletContext()).getContextHandler().handle(run); 912 } 913 }); 914 } 915 } 916 917 /* ------------------------------------------------------------ */ 918 public boolean hasOriginalRequestAndResponse() 919 { 920 synchronized (this) 921 { 922 return (_event!=null && _event.getSuppliedRequest()==_connection._request && _event.getSuppliedResponse()==_connection._response); 923 } 924 } 925 926 /* ------------------------------------------------------------ */ 927 public ContextHandler getContextHandler() 928 { 929 final AsyncEventState event=_event; 930 if (event!=null) 931 return ((Context)event.getServletContext()).getContextHandler(); 932 return null; 933 } 934 935 936 /* ------------------------------------------------------------ */ 937 /** 938 * @see Continuation#isResumed() 939 */ 940 public boolean isResumed() 941 { 942 synchronized (this) 943 { 944 return _resumed; 945 } 946 } 947 /* ------------------------------------------------------------ */ 948 /** 949 * @see Continuation#isExpired() 950 */ 951 public boolean isExpired() 952 { 953 synchronized (this) 954 { 955 return _expired; 956 } 957 } 958 959 /* ------------------------------------------------------------ */ 960 /** 961 * @see Continuation#resume() 962 */ 963 public void resume() 964 { 965 dispatch(); 966 } 967 968 969 970 /* ------------------------------------------------------------ */ 971 protected void startAsync(final ServletContext context, 972 final ServletRequest request, 973 final ServletResponse response) 974 { 975 synchronized (this) 976 { 977 _responseWrapped=!(response instanceof Response); 978 doSuspend(context,request,response); 979 if (request instanceof HttpServletRequest) 980 { 981 _event._pathInContext = URIUtil.addPaths(((HttpServletRequest)request).getServletPath(),((HttpServletRequest)request).getPathInfo()); 982 } 983 } 984 } 985 986 /* ------------------------------------------------------------ */ 987 protected void startAsync() 988 { 989 _responseWrapped=false; 990 _continuation=false; 991 doSuspend(_connection.getRequest().getServletContext(),_connection.getRequest(),_connection.getResponse()); 992 } 993 994 995 /* ------------------------------------------------------------ */ 996 /** 997 * @see Continuation#suspend() 998 */ 999 public void suspend(ServletResponse response) 1000 { 1001 _continuation=true; 1002 _responseWrapped=!(response instanceof Response); 1003 doSuspend(_connection.getRequest().getServletContext(),_connection.getRequest(),response); 1004 } 1005 1006 /* ------------------------------------------------------------ */ 1007 /** 1008 * @see Continuation#suspend() 1009 */ 1010 public void suspend() 1011 { 1012 _responseWrapped=false; 1013 _continuation=true; 1014 doSuspend(_connection.getRequest().getServletContext(),_connection.getRequest(),_connection.getResponse()); 1015 } 1016 1017 /* ------------------------------------------------------------ */ 1018 /** 1019 * @see org.eclipse.jetty.continuation.Continuation#getServletResponse() 1020 */ 1021 public ServletResponse getServletResponse() 1022 { 1023 if (_responseWrapped && _event!=null && _event.getSuppliedResponse()!=null) 1024 return _event.getSuppliedResponse(); 1025 return _connection.getResponse(); 1026 } 1027 1028 /* ------------------------------------------------------------ */ 1029 /** 1030 * @see org.eclipse.jetty.continuation.Continuation#getAttribute(java.lang.String) 1031 */ 1032 public Object getAttribute(String name) 1033 { 1034 return _connection.getRequest().getAttribute(name); 1035 } 1036 1037 /* ------------------------------------------------------------ */ 1038 /** 1039 * @see org.eclipse.jetty.continuation.Continuation#removeAttribute(java.lang.String) 1040 */ 1041 public void removeAttribute(String name) 1042 { 1043 _connection.getRequest().removeAttribute(name); 1044 } 1045 1046 /* ------------------------------------------------------------ */ 1047 /** 1048 * @see org.eclipse.jetty.continuation.Continuation#setAttribute(java.lang.String, java.lang.Object) 1049 */ 1050 public void setAttribute(String name, Object attribute) 1051 { 1052 _connection.getRequest().setAttribute(name,attribute); 1053 } 1054 1055 /* ------------------------------------------------------------ */ 1056 /** 1057 * @see org.eclipse.jetty.continuation.Continuation#undispatch() 1058 */ 1059 public void undispatch() 1060 { 1061 if (isSuspended()) 1062 { 1063 if (LOG.isDebugEnabled()) 1064 throw new ContinuationThrowable(); 1065 else 1066 throw __exception; 1067 } 1068 throw new IllegalStateException("!suspended"); 1069 } 1070 1071 /* ------------------------------------------------------------ */ 1072 /* ------------------------------------------------------------ */ 1073 public class AsyncTimeout extends Timeout.Task implements Runnable 1074 { 1075 @Override 1076 public void expired() 1077 { 1078 AsyncContinuation.this.expired(); 1079 } 1080 1081 @Override 1082 public void run() 1083 { 1084 AsyncContinuation.this.expired(); 1085 } 1086 } 1087 1088 /* ------------------------------------------------------------ */ 1089 /* ------------------------------------------------------------ */ 1090 public class AsyncEventState extends AsyncEvent 1091 { 1092 private final ServletContext _suspendedContext; 1093 private ServletContext _dispatchContext; 1094 private String _pathInContext; 1095 private Timeout.Task _timeout= new AsyncTimeout(); 1096 1097 public AsyncEventState(ServletContext context, ServletRequest request, ServletResponse response) 1098 { 1099 super(AsyncContinuation.this, request,response); 1100 _suspendedContext=context; 1101 // Get the base request So we can remember the initial paths 1102 Request r=_connection.getRequest(); 1103 1104 // If we haven't been async dispatched before 1105 if (r.getAttribute(AsyncContext.ASYNC_REQUEST_URI)==null) 1106 { 1107 // We are setting these attributes during startAsync, when the spec implies that 1108 // they are only available after a call to AsyncContext.dispatch(...); 1109 1110 // have we been forwarded before? 1111 String uri=(String)r.getAttribute(RequestDispatcher.FORWARD_REQUEST_URI); 1112 if (uri!=null) 1113 { 1114 r.setAttribute(AsyncContext.ASYNC_REQUEST_URI,uri); 1115 r.setAttribute(AsyncContext.ASYNC_CONTEXT_PATH,r.getAttribute(RequestDispatcher.FORWARD_CONTEXT_PATH)); 1116 r.setAttribute(AsyncContext.ASYNC_SERVLET_PATH,r.getAttribute(RequestDispatcher.FORWARD_SERVLET_PATH)); 1117 r.setAttribute(AsyncContext.ASYNC_PATH_INFO,r.getAttribute(RequestDispatcher.FORWARD_PATH_INFO)); 1118 r.setAttribute(AsyncContext.ASYNC_QUERY_STRING,r.getAttribute(RequestDispatcher.FORWARD_QUERY_STRING)); 1119 } 1120 else 1121 { 1122 r.setAttribute(AsyncContext.ASYNC_REQUEST_URI,r.getRequestURI()); 1123 r.setAttribute(AsyncContext.ASYNC_CONTEXT_PATH,r.getContextPath()); 1124 r.setAttribute(AsyncContext.ASYNC_SERVLET_PATH,r.getServletPath()); 1125 r.setAttribute(AsyncContext.ASYNC_PATH_INFO,r.getPathInfo()); 1126 r.setAttribute(AsyncContext.ASYNC_QUERY_STRING,r.getQueryString()); 1127 } 1128 } 1129 } 1130 1131 public ServletContext getSuspendedContext() 1132 { 1133 return _suspendedContext; 1134 } 1135 1136 public ServletContext getDispatchContext() 1137 { 1138 return _dispatchContext; 1139 } 1140 1141 public ServletContext getServletContext() 1142 { 1143 return _dispatchContext==null?_suspendedContext:_dispatchContext; 1144 } 1145 1146 public void setPath(String path) 1147 { 1148 _pathInContext=path; 1149 } 1150 1151 /* ------------------------------------------------------------ */ 1152 /** 1153 * @return The path in the context 1154 */ 1155 public String getPath() 1156 { 1157 return _pathInContext; 1158 } 1159 } 1160 } 1161