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 20package org.eclipse.jetty.continuation; 21 22import java.util.ArrayList; 23 24import javax.servlet.ServletRequest; 25import javax.servlet.ServletResponse; 26import javax.servlet.ServletResponseWrapper; 27 28import org.eclipse.jetty.continuation.ContinuationFilter.FilteredContinuation; 29 30 31/* ------------------------------------------------------------ */ 32/** 33 * A blocking implementation of Continuation. 34 * This implementation of Continuation is used by the {@link ContinuationFilter} 35 * when there are is no native or asynchronous continuation type available. 36 */ 37class FauxContinuation implements FilteredContinuation 38{ 39 // common exception used for all continuations. 40 // Turn on debug in ContinuationFilter to see real stack trace. 41 private final static ContinuationThrowable __exception = new ContinuationThrowable(); 42 43 private static final int __HANDLING=1; // Request dispatched to filter/servlet 44 private static final int __SUSPENDING=2; // Suspend called, but not yet returned to container 45 private static final int __RESUMING=3; // resumed while suspending 46 private static final int __COMPLETING=4; // resumed while suspending or suspended 47 private static final int __SUSPENDED=5; // Suspended and parked 48 private static final int __UNSUSPENDING=6; 49 private static final int __COMPLETE=7; 50 51 private final ServletRequest _request; 52 private ServletResponse _response; 53 54 private int _state=__HANDLING; 55 private boolean _initial=true; 56 private boolean _resumed=false; 57 private boolean _timeout=false; 58 private boolean _responseWrapped=false; 59 private long _timeoutMs=30000; // TODO configure 60 61 private ArrayList<ContinuationListener> _listeners; 62 63 FauxContinuation(final ServletRequest request) 64 { 65 _request=request; 66 } 67 68 /* ------------------------------------------------------------ */ 69 public void onComplete() 70 { 71 if (_listeners!=null) 72 for (ContinuationListener l:_listeners) 73 l.onComplete(this); 74 } 75 76 /* ------------------------------------------------------------ */ 77 public void onTimeout() 78 { 79 if (_listeners!=null) 80 for (ContinuationListener l:_listeners) 81 l.onTimeout(this); 82 } 83 84 /* ------------------------------------------------------------ */ 85 /** 86 * @see org.eclipse.jetty.continuation.Continuation#isResponseWrapped() 87 */ 88 public boolean isResponseWrapped() 89 { 90 return _responseWrapped; 91 } 92 93 /* ------------------------------------------------------------ */ 94 public boolean isInitial() 95 { 96 synchronized(this) 97 { 98 return _initial; 99 } 100 } 101 102 /* ------------------------------------------------------------ */ 103 public boolean isResumed() 104 { 105 synchronized(this) 106 { 107 return _resumed; 108 } 109 } 110 111 /* ------------------------------------------------------------ */ 112 public boolean isSuspended() 113 { 114 synchronized(this) 115 { 116 switch(_state) 117 { 118 case __HANDLING: 119 return false; 120 case __SUSPENDING: 121 case __RESUMING: 122 case __COMPLETING: 123 case __SUSPENDED: 124 return true; 125 case __UNSUSPENDING: 126 default: 127 return false; 128 } 129 } 130 } 131 132 /* ------------------------------------------------------------ */ 133 public boolean isExpired() 134 { 135 synchronized(this) 136 { 137 return _timeout; 138 } 139 } 140 141 /* ------------------------------------------------------------ */ 142 public void setTimeout(long timeoutMs) 143 { 144 _timeoutMs = timeoutMs; 145 } 146 147 /* ------------------------------------------------------------ */ 148 public void suspend(ServletResponse response) 149 { 150 _response=response; 151 _responseWrapped=response instanceof ServletResponseWrapper; 152 suspend(); 153 } 154 155 /* ------------------------------------------------------------ */ 156 public void suspend() 157 { 158 synchronized (this) 159 { 160 switch(_state) 161 { 162 case __HANDLING: 163 _timeout=false; 164 _resumed=false; 165 _state=__SUSPENDING; 166 return; 167 168 case __SUSPENDING: 169 case __RESUMING: 170 return; 171 172 case __COMPLETING: 173 case __SUSPENDED: 174 case __UNSUSPENDING: 175 throw new IllegalStateException(this.getStatusString()); 176 177 default: 178 throw new IllegalStateException(""+_state); 179 } 180 181 } 182 } 183 184 185 /* ------------------------------------------------------------ */ 186 /* (non-Javadoc) 187 * @see org.mortbay.jetty.Suspendor#resume() 188 */ 189 public void resume() 190 { 191 synchronized (this) 192 { 193 switch(_state) 194 { 195 case __HANDLING: 196 _resumed=true; 197 return; 198 199 case __SUSPENDING: 200 _resumed=true; 201 _state=__RESUMING; 202 return; 203 204 case __RESUMING: 205 case __COMPLETING: 206 return; 207 208 case __SUSPENDED: 209 fauxResume(); 210 _resumed=true; 211 _state=__UNSUSPENDING; 212 break; 213 214 case __UNSUSPENDING: 215 _resumed=true; 216 return; 217 218 default: 219 throw new IllegalStateException(this.getStatusString()); 220 } 221 } 222 223 } 224 225 226 /* ------------------------------------------------------------ */ 227 public void complete() 228 { 229 // just like resume, except don't set _resumed=true; 230 synchronized (this) 231 { 232 switch(_state) 233 { 234 case __HANDLING: 235 throw new IllegalStateException(this.getStatusString()); 236 237 case __SUSPENDING: 238 _state=__COMPLETING; 239 break; 240 241 case __RESUMING: 242 break; 243 244 case __COMPLETING: 245 return; 246 247 case __SUSPENDED: 248 _state=__COMPLETING; 249 fauxResume(); 250 break; 251 252 case __UNSUSPENDING: 253 return; 254 255 default: 256 throw new IllegalStateException(this.getStatusString()); 257 } 258 } 259 } 260 261 /* ------------------------------------------------------------ */ 262 /** 263 * @see org.eclipse.jetty.continuation.Continuation#getServletResponse() 264 */ 265 public boolean enter(ServletResponse response) 266 { 267 _response=response; 268 return true; 269 } 270 271 /* ------------------------------------------------------------ */ 272 /** 273 * @see org.eclipse.jetty.continuation.Continuation#getServletResponse() 274 */ 275 public ServletResponse getServletResponse() 276 { 277 return _response; 278 } 279 280 281 /* ------------------------------------------------------------ */ 282 void handling() 283 { 284 synchronized (this) 285 { 286 _responseWrapped=false; 287 switch(_state) 288 { 289 case __HANDLING: 290 throw new IllegalStateException(this.getStatusString()); 291 292 case __SUSPENDING: 293 case __RESUMING: 294 throw new IllegalStateException(this.getStatusString()); 295 296 case __COMPLETING: 297 return; 298 299 case __SUSPENDED: 300 fauxResume(); 301 case __UNSUSPENDING: 302 _state=__HANDLING; 303 return; 304 305 default: 306 throw new IllegalStateException(""+_state); 307 } 308 309 } 310 } 311 312 /* ------------------------------------------------------------ */ 313 /** 314 * @return true if handling is complete 315 */ 316 public boolean exit() 317 { 318 synchronized (this) 319 { 320 switch(_state) 321 { 322 case __HANDLING: 323 _state=__COMPLETE; 324 onComplete(); 325 return true; 326 327 case __SUSPENDING: 328 _initial=false; 329 _state=__SUSPENDED; 330 fauxSuspend(); // could block and change state. 331 if (_state==__SUSPENDED || _state==__COMPLETING) 332 { 333 onComplete(); 334 return true; 335 } 336 337 _initial=false; 338 _state=__HANDLING; 339 return false; 340 341 case __RESUMING: 342 _initial=false; 343 _state=__HANDLING; 344 return false; 345 346 case __COMPLETING: 347 _initial=false; 348 _state=__COMPLETE; 349 onComplete(); 350 return true; 351 352 case __SUSPENDED: 353 case __UNSUSPENDING: 354 default: 355 throw new IllegalStateException(this.getStatusString()); 356 } 357 } 358 } 359 360 /* ------------------------------------------------------------ */ 361 protected void expire() 362 { 363 // just like resume, except don't set _resumed=true; 364 365 synchronized (this) 366 { 367 _timeout=true; 368 } 369 370 onTimeout(); 371 372 synchronized (this) 373 { 374 switch(_state) 375 { 376 case __HANDLING: 377 return; 378 379 case __SUSPENDING: 380 _timeout=true; 381 _state=__RESUMING; 382 fauxResume(); 383 return; 384 385 case __RESUMING: 386 return; 387 388 case __COMPLETING: 389 return; 390 391 case __SUSPENDED: 392 _timeout=true; 393 _state=__UNSUSPENDING; 394 break; 395 396 case __UNSUSPENDING: 397 _timeout=true; 398 return; 399 400 default: 401 throw new IllegalStateException(this.getStatusString()); 402 } 403 } 404 } 405 406 private void fauxSuspend() 407 { 408 long expire_at = System.currentTimeMillis()+_timeoutMs; 409 long wait=_timeoutMs; 410 while (_timeoutMs>0 && wait>0) 411 { 412 try 413 { 414 this.wait(wait); 415 } 416 catch (InterruptedException e) 417 { 418 break; 419 } 420 wait=expire_at-System.currentTimeMillis(); 421 } 422 423 if (_timeoutMs>0 && wait<=0) 424 expire(); 425 } 426 427 private void fauxResume() 428 { 429 _timeoutMs=0; 430 this.notifyAll(); 431 } 432 433 @Override 434 public String toString() 435 { 436 return getStatusString(); 437 } 438 439 String getStatusString() 440 { 441 synchronized (this) 442 { 443 return 444 ((_state==__HANDLING)?"HANDLING": 445 (_state==__SUSPENDING)?"SUSPENDING": 446 (_state==__SUSPENDED)?"SUSPENDED": 447 (_state==__RESUMING)?"RESUMING": 448 (_state==__UNSUSPENDING)?"UNSUSPENDING": 449 (_state==__COMPLETING)?"COMPLETING": 450 ("???"+_state))+ 451 (_initial?",initial":"")+ 452 (_resumed?",resumed":"")+ 453 (_timeout?",timeout":""); 454 } 455 } 456 457 458 public void addContinuationListener(ContinuationListener listener) 459 { 460 if (_listeners==null) 461 _listeners=new ArrayList<ContinuationListener>(); 462 _listeners.add(listener); 463 464 } 465 466 /* ------------------------------------------------------------ */ 467 /** 468 * @see org.eclipse.jetty.continuation.Continuation#getAttribute(java.lang.String) 469 */ 470 public Object getAttribute(String name) 471 { 472 return _request.getAttribute(name); 473 } 474 475 /* ------------------------------------------------------------ */ 476 /** 477 * @see org.eclipse.jetty.continuation.Continuation#removeAttribute(java.lang.String) 478 */ 479 public void removeAttribute(String name) 480 { 481 _request.removeAttribute(name); 482 } 483 484 /* ------------------------------------------------------------ */ 485 /** 486 * @see org.eclipse.jetty.continuation.Continuation#setAttribute(java.lang.String, java.lang.Object) 487 */ 488 public void setAttribute(String name, Object attribute) 489 { 490 _request.setAttribute(name,attribute); 491 } 492 493 /* ------------------------------------------------------------ */ 494 /** 495 * @see org.eclipse.jetty.continuation.Continuation#undispatch() 496 */ 497 public void undispatch() 498 { 499 if (isSuspended()) 500 { 501 if (ContinuationFilter.__debug) 502 throw new ContinuationThrowable(); 503 throw __exception; 504 } 505 throw new IllegalStateException("!suspended"); 506 507 } 508} 509