1 // ========================================================================
2 // Copyright (c) 2007-2009 Mort Bay Consulting Pty. Ltd.
3 // ------------------------------------------------------------------------
4 // All rights reserved. This program and the accompanying materials
5 // are made available under the terms of the Eclipse Public License v1.0
6 // and Apache License v2.0 which accompanies this distribution.
7 // The Eclipse Public License is available at
8 // http://www.eclipse.org/legal/epl-v10.html
9 // The Apache License v2.0 is available at
10 // http://www.opensource.org/licenses/apache2.0.php
11 // You may elect to redistribute this code under either of these licenses.
12 // ========================================================================
13
14 package org.eclipse.jetty.server;
15
16 import javax.servlet.ServletContext;
17 import javax.servlet.ServletRequest;
18 import javax.servlet.ServletResponse;
19
20 import org.eclipse.jetty.continuation.ContinuationEvent;
21 import org.eclipse.jetty.continuation.ContinuationListener;
22 import org.eclipse.jetty.continuation.Continuation;
23 import org.eclipse.jetty.io.AsyncEndPoint;
24 import org.eclipse.jetty.io.EndPoint;
25 import org.eclipse.jetty.server.handler.ContextHandler;
26 import org.eclipse.jetty.server.handler.ContextHandler.Context;
27 import org.eclipse.jetty.util.LazyList;
28 import org.eclipse.jetty.util.log.Log;
29 import org.eclipse.jetty.util.thread.Timeout;
30
31 /* ------------------------------------------------------------ */
32 /** Asyncrhonous Request.
33 *
34 */
35 public class AsyncRequest implements AsyncContext, Continuation
36 {
37 // STATES:
38 private static final int __IDLE=0; // Idle request
39 private static final int __DISPATCHED=1; // Request dispatched to filter/servlet
40 private static final int __SUSPENDING=2; // Suspend called, but not yet returned to container
41 private static final int __REDISPATCHING=3;// resumed while dispatched
42 private static final int __SUSPENDED=4; // Suspended and parked
43 private static final int __UNSUSPENDING=5; // Has been scheduled
44 private static final int __REDISPATCHED=6; // Request redispatched to filter/servlet
45 private static final int __COMPLETING=7; // complete while dispatched
46 private static final int __UNCOMPLETED=8; // Request is completable
47 private static final int __COMPLETE=9; // Request is complete
48
49 // State table
50 // __HANDLE __UNHANDLE __SUSPEND __REDISPATCH
51 // IDLE */ { __DISPATCHED, __Illegal, __Illegal, __Illegal },
52 // DISPATCHED */ { __Illegal, __UNCOMPLETED, __SUSPENDING, __Ignore },
53 // SUSPENDING */ { __Illegal, __SUSPENDED, __Illegal,__REDISPATCHING },
54 // REDISPATCHING */ { __Illegal, _REDISPATCHED, __Ignored, __Ignore },
55 // COMPLETING */ { __Illegal, __UNCOMPLETED, __Illegal, __Illegal },
56 // SUSPENDED */ { __REDISPATCHED, __Illegal, __Illegal, __UNSUSPENDING },
57 // UNSUSPENDING */ { __REDISPATCHED, __Illegal, __Illegal, __Ignore },
58 // REDISPATCHED */ { __Illegal, __UNCOMPLETED, __SUSPENDING, __Ignore },
59
60
61 /* ------------------------------------------------------------ */
62 protected HttpConnection _connection;
63 private Object _listeners;
64
65 /* ------------------------------------------------------------ */
66 private int _state;
67 private boolean _initial;
68 private boolean _resumed;
69 private boolean _expired;
70 private boolean _keepWrappers;
71 private long _timeoutMs;
72 private AsyncEventState _event;
73
74 /* ------------------------------------------------------------ */
75 protected AsyncRequest()
76 {
77 _state=__IDLE;
78 _initial=true;
79 }
80
81 /* ------------------------------------------------------------ */
82 protected void setConnection(final HttpConnection connection)
83 {
84 _connection=connection;
85 }
86
87 /* ------------------------------------------------------------ */
88 public void addContinuationListener(ContinuationListener listener)
89 {
90 _listeners=LazyList.add(_listeners,listener);
91 }
92
93 /* ------------------------------------------------------------ */
94 public void setAsyncTimeout(long ms)
95 {
96 _timeoutMs=ms;
97 }
98
99 /* ------------------------------------------------------------ */
100 public long getAsyncTimeout()
101 {
102 return _timeoutMs;
103 }
104
105 /* ------------------------------------------------------------ */
106 public AsyncEventState getAsyncEventState()
107 {
108 return _event;
109 }
110
111 /* ------------------------------------------------------------ */
112 /**
113 * @see org.eclipse.jetty.continuation.Continuation#keepWrappers()
114 */
115 public void keepWrappers()
116 {
117 _keepWrappers=true;
118 }
119
120 /* ------------------------------------------------------------ */
121 /**
122 * @see org.eclipse.jetty.continuation.Continuation#wrappersKept()
123 */
124 public boolean wrappersKept()
125 {
126 return _keepWrappers;
127 }
128
129 /* ------------------------------------------------------------ */
130 /* (non-Javadoc)
131 * @see javax.servlet.ServletRequest#isInitial()
132 */
133 public boolean isInitial()
134 {
135 synchronized(this)
136 {
137 return _initial;
138 }
139 }
140
141 /* ------------------------------------------------------------ */
142 /* (non-Javadoc)
143 * @see javax.servlet.ServletRequest#isSuspended()
144 */
145 public boolean isSuspended()
146 {
147 synchronized(this)
148 {
149 switch(_state)
150 {
151 case __SUSPENDING:
152 case __REDISPATCHING:
153 case __COMPLETING:
154 case __SUSPENDED:
155 return true;
156
157 default:
158 return false;
159 }
160 }
161 }
162
163 /* ------------------------------------------------------------ */
164 public String toString()
165 {
166 return getStatusString();
167 }
168
169 /* ------------------------------------------------------------ */
170 public String getStatusString()
171 {
172 synchronized (this)
173 {
174 return
175 ((_state==__IDLE)?"IDLE":
176 (_state==__DISPATCHED)?"DISPATCHED":
177 (_state==__SUSPENDING)?"SUSPENDING":
178 (_state==__SUSPENDED)?"SUSPENDED":
179 (_state==__REDISPATCHING)?"REDISPATCHING":
180 (_state==__UNSUSPENDING)?"UNSUSPENDING":
181 (_state==__REDISPATCHED)?"REDISPATCHED":
182 (_state==__COMPLETING)?"COMPLETING":
183 (_state==__UNCOMPLETED)?"UNCOMPLETED":
184 (_state==__COMPLETE)?"COMPLETE":
185 ("UNKNOWN?"+_state))+
186 (_initial?",initial":"");
187 }
188 }
189
190 /* ------------------------------------------------------------ */
191 /* (non-Javadoc)
192 * @see javax.servlet.ServletRequest#resume()
193 */
194 protected boolean handling()
195 {
196 synchronized (this)
197 {
198 _keepWrappers=false;
199
200 switch(_state)
201 {
202 case __DISPATCHED:
203 case __REDISPATCHED:
204 case __COMPLETE:
205 throw new IllegalStateException(this.getStatusString());
206
207 case __IDLE:
208 _initial=true;
209 _state=__DISPATCHED;
210 return true;
211
212 case __SUSPENDING:
213 case __REDISPATCHING:
214 throw new IllegalStateException(this.getStatusString());
215
216 case __COMPLETING:
217 _state=__UNCOMPLETED;
218 return false;
219
220 case __SUSPENDED:
221 cancelTimeout();
222 case __UNSUSPENDING:
223 _state=__REDISPATCHED;
224 return true;
225
226 default:
227 throw new IllegalStateException(""+_state);
228 }
229 }
230 }
231
232 /* ------------------------------------------------------------ */
233 /* (non-Javadoc)
234 * @see javax.servlet.ServletRequest#suspend(long)
235 */
236 protected void suspend(final ServletContext context,
237 final ServletRequest request,
238 final ServletResponse response)
239 {
240 synchronized (this)
241 {
242 _resumed=false;
243 _expired=false;
244
245 if (_event==null || request!=_event.getRequest() || response != _event.getResponse() || context != _event.getServletContext())
246 _event=new AsyncEventState(context,request,response);
247 else
248 {
249 _event._dispatchContext=null;
250 _event._path=null;
251 }
252
253 switch(_state)
254 {
255 case __DISPATCHED:
256 case __REDISPATCHED:
257 _state=__SUSPENDING;
258 return;
259
260 case __IDLE:
261 throw new IllegalStateException(this.getStatusString());
262
263 case __SUSPENDING:
264 case __REDISPATCHING:
265 return;
266
267 case __COMPLETING:
268 case __SUSPENDED:
269 case __UNSUSPENDING:
270 case __COMPLETE:
271 throw new IllegalStateException(this.getStatusString());
272
273 default:
274 throw new IllegalStateException(""+_state);
275 }
276 }
277 }
278
279 /* ------------------------------------------------------------ */
280 /**
281 * Signal that the HttpConnection has finished handling the request.
282 * For blocking connectors, this call may block if the request has
283 * been suspended (startAsync called).
284 * @return true if handling is complete, false if the request should
285 * be handled again (eg because of a resume that happened before unhandle was called)
286 */
287 protected boolean unhandle()
288 {
289 synchronized (this)
290 {
291 switch(_state)
292 {
293 case __REDISPATCHED:
294 case __DISPATCHED:
295 _state=__UNCOMPLETED;
296 return true;
297
298 case __IDLE:
299 throw new IllegalStateException(this.getStatusString());
300
301 case __SUSPENDING:
302 _initial=false;
303 _state=__SUSPENDED;
304 scheduleTimeout(); // could block and change state.
305 if (_state==__SUSPENDED)
306 return true;
307 else if (_state==__COMPLETING)
308 {
309 _state=__UNCOMPLETED;
310 return true;
311 }
312 _initial=false;
313 _state=__REDISPATCHED;
314 return false;
315
316 case __REDISPATCHING:
317 _initial=false;
318 _state=__REDISPATCHED;
319 return false;
320
321 case __COMPLETING:
322 _initial=false;
323 _state=__UNCOMPLETED;
324 return true;
325
326 case __SUSPENDED:
327 case __UNSUSPENDING:
328 default:
329 throw new IllegalStateException(this.getStatusString());
330 }
331 }
332 }
333
334 /* ------------------------------------------------------------ */
335 public void dispatch()
336 {
337 boolean dispatch=false;
338 synchronized (this)
339 {
340 switch(_state)
341 {
342 case __REDISPATCHED:
343 case __DISPATCHED:
344 case __IDLE:
345 case __REDISPATCHING:
346 case __COMPLETING:
347 case __COMPLETE:
348 case __UNCOMPLETED:
349 return;
350
351 case __SUSPENDING:
352 _state=__REDISPATCHING;
353 _resumed=true;
354 return;
355
356 case __SUSPENDED:
357 dispatch=true;
358 _state=__UNSUSPENDING;
359 _resumed=true;
360 break;
361
362 case __UNSUSPENDING:
363 return;
364
365 default:
366 throw new IllegalStateException(this.getStatusString());
367 }
368 }
369
370 if (dispatch)
371 {
372 cancelTimeout();
373 scheduleDispatch();
374 }
375 }
376
377 /* ------------------------------------------------------------ */
378 protected void expired()
379 {
380 synchronized (this)
381 {
382 switch(_state)
383 {
384 case __SUSPENDING:
385 case __SUSPENDED:
386 break;
387 default:
388 return;
389 }
390 _expired=true;
391 }
392
393 if (_listeners!=null)
394 {
395 for(int i=0;i<LazyList.size(_listeners);i++)
396 {
397 try
398 {
399 ContinuationListener listener=((ContinuationListener)LazyList.get(_listeners,i));
400 listener.onTimeout(_event);
401 }
402 catch(Exception e)
403 {
404 Log.warn(e);
405 }
406 }
407 }
408
409 synchronized (this)
410 {
411 switch(_state)
412 {
413 case __SUSPENDING:
414 case __SUSPENDED:
415 dispatch();
416 return;
417
418 default:
419 return;
420 }
421 }
422 }
423
424 /* ------------------------------------------------------------ */
425 /* (non-Javadoc)
426 * @see javax.servlet.ServletRequest#complete()
427 */
428 public void complete()
429 {
430 // just like resume, except don't set _resumed=true;
431 boolean dispatch=false;
432 synchronized (this)
433 {
434 switch(_state)
435 {
436 case __IDLE:
437 case __COMPLETE:
438 case __REDISPATCHING:
439 case __COMPLETING:
440 case __UNSUSPENDING:
441 return;
442
443 case __DISPATCHED:
444 case __REDISPATCHED:
445 throw new IllegalStateException(this.getStatusString());
446
447 case __SUSPENDING:
448 _state=__COMPLETING;
449 return;
450
451 case __SUSPENDED:
452 _state=__COMPLETING;
453 dispatch=true;
454 break;
455
456 default:
457 throw new IllegalStateException(this.getStatusString());
458 }
459 }
460
461 if (dispatch)
462 {
463 cancelTimeout();
464 scheduleDispatch();
465 }
466 }
467
468
469 /* ------------------------------------------------------------ */
470 /* (non-Javadoc)
471 * @see javax.servlet.ServletRequest#complete()
472 */
473 protected void doComplete()
474 {
475 synchronized (this)
476 {
477 switch(_state)
478 {
479 case __UNCOMPLETED:
480 _state=__COMPLETE;
481 break;
482
483 default:
484 throw new IllegalStateException(this.getStatusString());
485 }
486 }
487
488 if (_listeners!=null)
489 {
490 for(int i=0;i<LazyList.size(_listeners);i++)
491 {
492 try
493 {
494 ((ContinuationListener)LazyList.get(_listeners,i)).onComplete(_event);
495 }
496 catch(Exception e)
497 {
498 Log.warn(e);
499 }
500 }
501 }
502 }
503
504 /* ------------------------------------------------------------ */
505 protected void recycle()
506 {
507 synchronized (this)
508 {
509 switch(_state)
510 {
511 case __DISPATCHED:
512 case __REDISPATCHED:
513 throw new IllegalStateException(getStatusString());
514 default:
515 _state=__IDLE;
516 }
517 _initial = true;
518 _resumed=false;
519 _expired=false;
520 _keepWrappers=false;
521 cancelTimeout();
522 _timeoutMs=60000L; // TODO configure
523 _listeners=null;
524 }
525 }
526
527 /* ------------------------------------------------------------ */
528 public void cancel()
529 {
530 synchronized (this)
531 {
532 _state=__COMPLETE;
533 _initial = false;
534 cancelTimeout();
535 _listeners=null;
536 }
537 }
538
539 /* ------------------------------------------------------------ */
540 protected void scheduleDispatch()
541 {
542 EndPoint endp=_connection.getEndPoint();
543 if (!endp.isBlocking())
544 {
545 ((AsyncEndPoint)endp).dispatch();
546 }
547 }
548
549 /* ------------------------------------------------------------ */
550 protected void scheduleTimeout()
551 {
552 EndPoint endp=_connection.getEndPoint();
553 if (endp.isBlocking())
554 {
555 synchronized(this)
556 {
557 long expire_at = System.currentTimeMillis()+_timeoutMs;
558 long wait=_timeoutMs;
559 while (_timeoutMs>0 && wait>0)
560 {
561 try
562 {
563 this.wait(wait);
564 }
565 catch (InterruptedException e)
566 {
567 Log.ignore(e);
568 }
569 wait=expire_at-System.currentTimeMillis();
570 }
571
572 if (_timeoutMs>0 && wait<=0)
573 expired();
574 }
575 }
576 else
577 _connection.scheduleTimeout(_event._timeout,_timeoutMs);
578 }
579
580 /* ------------------------------------------------------------ */
581 protected void cancelTimeout()
582 {
583 EndPoint endp=_connection.getEndPoint();
584 if (endp.isBlocking())
585 {
586 synchronized(this)
587 {
588 _timeoutMs=0;
589 this.notifyAll();
590 }
591 }
592 else
593 {
594 final AsyncEventState event=_event;
595 if (event!=null)
596 _connection.cancelTimeout(event._timeout);
597 }
598 }
599
600 /* ------------------------------------------------------------ */
601 public boolean isCompleting()
602 {
603 return _state==__COMPLETING;
604 }
605
606 /* ------------------------------------------------------------ */
607 boolean isUncompleted()
608 {
609 return _state==__UNCOMPLETED;
610 }
611
612 /* ------------------------------------------------------------ */
613 public boolean isComplete()
614 {
615 return _state==__COMPLETE;
616 }
617
618
619 /* ------------------------------------------------------------ */
620 public boolean isAsyncStarted()
621 {
622 switch(_state)
623 {
624 case __SUSPENDING:
625 case __REDISPATCHING:
626 case __UNSUSPENDING:
627 case __SUSPENDED:
628 return true;
629
630 default:
631 return false;
632 }
633 }
634
635
636 /* ------------------------------------------------------------ */
637 public boolean isAsync()
638 {
639 switch(_state)
640 {
641 case __IDLE:
642 case __DISPATCHED:
643 return false;
644
645 default:
646 return true;
647 }
648 }
649
650 /* ------------------------------------------------------------ */
651 public void dispatch(ServletContext context, String path)
652 {
653 _event._dispatchContext=context;
654 _event._path=path;
655 dispatch();
656 }
657
658 /* ------------------------------------------------------------ */
659 public void dispatch(String path)
660 {
661 _event._path=path;
662 dispatch();
663 }
664
665 /* ------------------------------------------------------------ */
666 public ServletRequest getRequest()
667 {
668 if (_event!=null)
669 return _event.getRequest();
670 return _connection.getRequest();
671 }
672
673 /* ------------------------------------------------------------ */
674 public ServletResponse getResponse()
675 {
676 if (_event!=null)
677 return _event.getResponse();
678 return _connection.getResponse();
679 }
680
681 /* ------------------------------------------------------------ */
682 public void start(Runnable run)
683 {
684 ((Context)_event.getServletContext()).getContextHandler().handle(run);
685 }
686
687 /* ------------------------------------------------------------ */
688 public boolean hasOriginalRequestAndResponse()
689 {
690 return (_event!=null && _event.getRequest()==_connection._request && _event.getResponse()==_connection._response);
691 }
692
693 /* ------------------------------------------------------------ */
694 public ContextHandler getContextHandler()
695 {
696 if (_event!=null)
697 return ((Context)_event.getServletContext()).getContextHandler();
698 return null;
699 }
700
701
702 /* ------------------------------------------------------------ */
703 /**
704 * @see Continuation#isResumed()
705 */
706 public boolean isResumed()
707 {
708 return _resumed;
709 }
710 /* ------------------------------------------------------------ */
711 /**
712 * @see Continuation#isExpired()
713 */
714 public boolean isExpired()
715 {
716 return _expired;
717 }
718
719 /* ------------------------------------------------------------ */
720 /**
721 * @see Continuation#resume()
722 */
723 public void resume()
724 {
725 dispatch();
726 }
727
728 /* ------------------------------------------------------------ */
729 /**
730 * @see Continuation#suspend(long)
731 */
732 public void setTimeout(long timeoutMs)
733 {
734 setAsyncTimeout(timeoutMs);
735 }
736
737 /* ------------------------------------------------------------ */
738 /**
739 * @see Continuation#suspend()
740 */
741 public void suspend()
742 {
743 // TODO simplify?
744 AsyncRequest.this.suspend(_connection.getRequest().getServletContext(),_connection.getRequest(),_connection.getResponse());
745 }
746
747 /* ------------------------------------------------------------ */
748 /**
749 * @see org.eclipse.jetty.continuation.Continuation#getServletRequest()
750 */
751 public ServletRequest getServletRequest()
752 {
753 return _connection.getRequest();
754 }
755
756 /* ------------------------------------------------------------ */
757 /**
758 * @see org.eclipse.jetty.continuation.Continuation#getServletResponse()
759 */
760 public ServletResponse getServletResponse()
761 {
762 return _connection.getResponse();
763 }
764
765 /* ------------------------------------------------------------ */
766 /* ------------------------------------------------------------ */
767 public class AsyncEventState implements ContinuationEvent
768 {
769 private final ServletContext _suspendedContext;
770 private final ServletRequest _request;
771 private final ServletResponse _response;
772
773 ServletContext _dispatchContext;
774
775 String _path;
776 final Timeout.Task _timeout = new Timeout.Task()
777 {
778 public void expired()
779 {
780 AsyncRequest.this.expired();
781 }
782 };
783
784 public AsyncEventState(ServletContext context, ServletRequest request, ServletResponse response)
785 {
786 _suspendedContext=context;
787 _request=request;
788 _response=response;
789 }
790
791 public ServletContext getSuspendedContext()
792 {
793 return _suspendedContext;
794 }
795
796 public ServletContext getDispatchContext()
797 {
798 return _dispatchContext;
799 }
800
801 public ServletContext getServletContext()
802 {
803 return _dispatchContext==null?_suspendedContext:_dispatchContext;
804 }
805
806 public ServletRequest getRequest()
807 {
808 return _request;
809 }
810
811 public ServletResponse getResponse()
812 {
813 return _response;
814 }
815
816 public String getPath()
817 {
818 return _path;
819 }
820 }
821 }