All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.jetty.server.HttpChannelState Maven / Gradle / Ivy

There is a newer version: 12.0.13
Show newest version
//
//  ========================================================================
//  Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
//  ------------------------------------------------------------------------
//  All rights reserved. This program and the accompanying materials
//  are made available under the terms of the Eclipse Public License v1.0
//  and Apache License v2.0 which accompanies this distribution.
//
//      The Eclipse Public License is available at
//      http://www.eclipse.org/legal/epl-v10.html
//
//      The Apache License v2.0 is available at
//      http://www.opensource.org/licenses/apache2.0.php
//
//  You may elect to redistribute this code under either of these licenses.
//  ========================================================================
//

package org.eclipse.jetty.server;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletResponse;

import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.ContextHandler.Context;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.thread.Scheduler;

/**
 * Implementation of AsyncContext interface that holds the state of request-response cycle.
 *
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
 * 
STATEACTION
handling() startAsync()unhandle() dispatch() complete() completed()
IDLE: DISPATCHED COMPLETECALLED??
DISPATCHED: ASYNCSTARTEDCOMPLETING
ASYNCSTARTED: ASYNCWAIT REDISPATCHINGCOMPLETECALLED
REDISPATCHING: REDISPATCHED
ASYNCWAIT: REDISPATCH COMPLETECALLED
REDISPATCH: REDISPATCHED
REDISPATCHED: ASYNCSTARTEDCOMPLETING
COMPLETECALLED:COMPLETING COMPLETING
COMPLETING: COMPLETING COMPLETED
COMPLETED:
*/ public class HttpChannelState { private static final Logger LOG = Log.getLogger(HttpChannelState.class); private final static long DEFAULT_TIMEOUT=30000L; public enum State { IDLE, // Idle request DISPATCHED, // Request dispatched to filter/servlet ASYNCSTARTED, // Suspend called, but not yet returned to container REDISPATCHING, // resumed while dispatched ASYNCWAIT, // Suspended and parked REDISPATCH, // Has been scheduled REDISPATCHED, // Request redispatched to filter/servlet COMPLETECALLED,// complete called COMPLETING, // Request is completable COMPLETED // Request is complete } public enum Next { CONTINUE, // Continue handling the channel WAIT, // Wait for further events COMPLETE // Complete the channel } private final HttpChannel _channel; private List _lastAsyncListeners; private List _asyncListeners; private State _state; private boolean _initial; private boolean _dispatched; private boolean _expired; private volatile boolean _responseWrapped; private long _timeoutMs=DEFAULT_TIMEOUT; private AsyncContextEvent _event; protected HttpChannelState(HttpChannel channel) { _channel=channel; _state=State.IDLE; _initial=true; } public State getState() { synchronized(this) { return _state; } } public void addListener(AsyncListener listener) { synchronized(this) { if (_asyncListeners==null) _asyncListeners=new ArrayList<>(); _asyncListeners.add(listener); } } public void setTimeout(long ms) { synchronized(this) { _timeoutMs=ms; } } public long getTimeout() { synchronized(this) { return _timeoutMs; } } public AsyncContextEvent getAsyncContextEvent() { synchronized(this) { return _event; } } @Override public String toString() { synchronized (this) { return super.toString()+"@"+getStatusString(); } } public String getStatusString() { synchronized (this) { return _state+ (_initial?",initial":"")+ (_dispatched?",resumed":"")+ (_expired?",expired":""); } } /** * @return Next handling of the request should proceed */ protected Next handling() { synchronized (this) { switch(_state) { case IDLE: _initial=true; _state=State.DISPATCHED; if (_lastAsyncListeners!=null) _lastAsyncListeners.clear(); if (_asyncListeners!=null) _asyncListeners.clear(); else { _asyncListeners=_lastAsyncListeners; _lastAsyncListeners=null; } break; case COMPLETECALLED: _state=State.COMPLETING; return Next.COMPLETE; case COMPLETING: return Next.COMPLETE; case ASYNCWAIT: return Next.WAIT; case COMPLETED: return Next.WAIT; case REDISPATCH: _state=State.REDISPATCHED; break; default: throw new IllegalStateException(this.getStatusString()); } _responseWrapped=false; return Next.CONTINUE; } } public void startAsync(AsyncContextEvent event) { synchronized (this) { switch(_state) { case DISPATCHED: case REDISPATCHED: _dispatched=false; _expired=false; _responseWrapped=event.getSuppliedResponse()!=_channel.getResponse(); _responseWrapped=false; _event=event; _state=State.ASYNCSTARTED; List listeners=_lastAsyncListeners; _lastAsyncListeners=_asyncListeners; if (listeners!=null) listeners.clear(); _asyncListeners=listeners; break; default: throw new IllegalStateException(this.getStatusString()); } } if (_lastAsyncListeners!=null) { for (AsyncListener listener : _lastAsyncListeners) { try { listener.onStartAsync(_event); } catch(Exception e) { LOG.warn(e); } } } } protected void error(Throwable th) { synchronized (this) { if (_event!=null) _event.setThrowable(th); } } /** * Signal that the HttpConnection has finished handling the request. * For blocking connectors, this call may block if the request has * been suspended (startAsync called). * @return next actions * be handled again (eg because of a resume that happened before unhandle was called) */ protected Next unhandle() { synchronized (this) { switch(_state) { case REDISPATCHED: case DISPATCHED: _state=State.COMPLETING; return Next.COMPLETE; case IDLE: throw new IllegalStateException(this.getStatusString()); case ASYNCSTARTED: _initial=false; _state=State.ASYNCWAIT; scheduleTimeout(); return Next.WAIT; case REDISPATCHING: _initial=false; _state=State.REDISPATCHED; return Next.CONTINUE; case COMPLETECALLED: _initial=false; _state=State.COMPLETING; return Next.COMPLETE; default: throw new IllegalStateException(this.getStatusString()); } } } public void dispatch(ServletContext context, String path) { boolean dispatch; synchronized (this) { switch(_state) { case ASYNCSTARTED: _state=State.REDISPATCHING; _event.setDispatchTarget(context,path); _dispatched=true; return; case ASYNCWAIT: dispatch=!_expired; _state=State.REDISPATCH; _event.setDispatchTarget(context,path); _dispatched=true; break; default: throw new IllegalStateException(this.getStatusString()); } } if (dispatch) { cancelTimeout(); scheduleDispatch(); } } public boolean isDispatched() { synchronized (this) { return _dispatched; } } protected void expired() { final List aListeners; AsyncContextEvent event; synchronized (this) { switch(_state) { case ASYNCSTARTED: case ASYNCWAIT: _expired=true; event=_event; aListeners=_asyncListeners; break; default: return; } } if (aListeners!=null) { for (AsyncListener listener : aListeners) { try { listener.onTimeout(event); } catch(Exception e) { LOG.debug(e); event.setThrowable(e); _channel.getRequest().setAttribute(RequestDispatcher.ERROR_EXCEPTION,e); break; } } } synchronized (this) { switch(_state) { case ASYNCSTARTED: case ASYNCWAIT: _state=State.REDISPATCH; break; default: _expired=false; break; } } scheduleDispatch(); } public void complete() { // just like resume, except don't set _dispatched=true; boolean handle; synchronized (this) { switch(_state) { case DISPATCHED: case REDISPATCHED: throw new IllegalStateException(this.getStatusString()); case IDLE: case ASYNCSTARTED: _state=State.COMPLETECALLED; return; case ASYNCWAIT: _state=State.COMPLETECALLED; handle=!_expired; break; default: throw new IllegalStateException(this.getStatusString()); } } if (handle) { cancelTimeout(); ContextHandler handler=getContextHandler(); if (handler!=null) handler.handle(_channel); else _channel.handle(); } } protected void completed() { final List aListeners; final AsyncContextEvent event; synchronized (this) { switch(_state) { case COMPLETING: _state=State.COMPLETED; aListeners=_asyncListeners; event=_event; break; default: throw new IllegalStateException(this.getStatusString()); } } if (aListeners!=null) { for (AsyncListener listener : aListeners) { try { if (event!=null && event.getThrowable()!=null) { event.getSuppliedRequest().setAttribute(RequestDispatcher.ERROR_EXCEPTION,event.getThrowable()); event.getSuppliedRequest().setAttribute(RequestDispatcher.ERROR_MESSAGE,event.getThrowable().getMessage()); listener.onError(event); } else listener.onComplete(event); } catch(Exception e) { LOG.warn(e); } } } if (event!=null) event.completed(); } protected void recycle() { synchronized (this) { switch(_state) { case DISPATCHED: case REDISPATCHED: throw new IllegalStateException(getStatusString()); default: _state=State.IDLE; } _initial = true; _dispatched=false; _expired=false; _responseWrapped=false; cancelTimeout(); _timeoutMs=DEFAULT_TIMEOUT; _event=null; } } protected void scheduleDispatch() { _channel.execute(_channel); } protected void scheduleTimeout() { Scheduler scheduler = _channel.getScheduler(); if (scheduler!=null && _timeoutMs>0) _event.setTimeoutTask(scheduler.schedule(new AsyncTimeout(),_timeoutMs,TimeUnit.MILLISECONDS)); } protected void cancelTimeout() { AsyncContextEvent event=_event; if (event!=null) event.cancelTimeoutTask(); } public boolean isExpired() { synchronized (this) { return _expired; } } public boolean isInitial() { synchronized(this) { return _initial; } } public boolean isSuspended() { synchronized(this) { switch(_state) { case ASYNCSTARTED: case REDISPATCHING: case COMPLETECALLED: case ASYNCWAIT: return true; default: return false; } } } boolean isCompleting() { synchronized (this) { return _state==State.COMPLETING; } } boolean isCompleted() { synchronized (this) { return _state == State.COMPLETED; } } public boolean isAsyncStarted() { synchronized (this) { switch(_state) { case ASYNCSTARTED: // Suspend called, but not yet returned to container case REDISPATCHING: // resumed while dispatched case COMPLETECALLED: // complete called case ASYNCWAIT: return true; default: return false; } } } public boolean isAsync() { synchronized (this) { switch(_state) { case ASYNCSTARTED: case REDISPATCHING: case ASYNCWAIT: case REDISPATCHED: case REDISPATCH: case COMPLETECALLED: return true; default: return false; } } } public Request getBaseRequest() { return _channel.getRequest(); } public HttpChannel getHttpChannel() { return _channel; } public ContextHandler getContextHandler() { final AsyncContextEvent event=_event; if (event!=null) { Context context=((Context)event.getServletContext()); if (context!=null) return context.getContextHandler(); } return null; } public ServletResponse getServletResponse() { if (_responseWrapped && _event!=null && _event.getSuppliedResponse()!=null) return _event.getSuppliedResponse(); return _channel.getResponse(); } public Object getAttribute(String name) { return _channel.getRequest().getAttribute(name); } public void removeAttribute(String name) { _channel.getRequest().removeAttribute(name); } public void setAttribute(String name, Object attribute) { _channel.getRequest().setAttribute(name,attribute); } public class AsyncTimeout implements Runnable { @Override public void run() { HttpChannelState.this.expired(); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy