view src/org/eclipse/jetty/server/AsyncContinuation.java @ 934:fe461f7cfc8e

simplify AsyncContinuation
author Franklin Schmidt <fschmidt@gmail.com>
date Sun, 09 Oct 2016 21:03:00 -0600
parents c9513d80f305
children aa7dc1802d29
line wrap: on
line source

//
//  ========================================================================
//  Copyright (c) 1995-2014 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 javax.servlet.AsyncListener;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;

import java.util.ArrayList;
import java.util.List;

import javax.servlet.ServletContext;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

import org.eclipse.jetty.continuation.Continuation;
import org.eclipse.jetty.continuation.ContinuationListener;
import org.eclipse.jetty.io.AsyncEndPoint;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.ContextHandler.Context;
import org.eclipse.jetty.util.URIUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/* ------------------------------------------------------------ */
/** Implementation of Continuation interfaces
 * 
 */
public class AsyncContinuation implements Continuation
{
	private static final Logger LOG = LoggerFactory.getLogger(AsyncContinuation.class);

	private final static long DEFAULT_TIMEOUT=30000L;
		
	// STATES:
	//               handling()    suspend()     unhandle()    resume()       complete()  doComplete()
	//                             startAsync()                dispatch()   
	// IDLE          DISPATCHED      
	// DISPATCHED                  ASYNCSTARTED  UNCOMPLETED
	// ASYNCSTARTED                              ASYNCWAIT     REDISPATCHING  COMPLETING
	// REDISPATCHING                             REDISPATCHED  
	// ASYNCWAIT                                               REDISPATCH     COMPLETING
	// REDISPATCH    REDISPATCHED
	// REDISPATCHED                ASYNCSTARTED  UNCOMPLETED
	// COMPLETING    UNCOMPLETED                 UNCOMPLETED
	// UNCOMPLETED                                                                        COMPLETED
	// COMPLETED
	private static final int __IDLE=0;         // Idle request
	private static final int __DISPATCHED=1;   // Request dispatched to filter/servlet
	private static final int __UNCOMPLETED=8;  // Request is completable
	private static final int __COMPLETED=9;    // Request is complete
	
	/* ------------------------------------------------------------ */
	protected AbstractHttpConnection _connection;
	private List<AsyncListener> _asyncListeners;
	private List<ContinuationListener> _continuationListeners;

	/* ------------------------------------------------------------ */
	private int _state;
	private volatile long _expireAt;    
	
	protected AsyncContinuation()
	{
		_state=__IDLE;
	}

	protected synchronized void setConnection(final AbstractHttpConnection connection)
	{
		_connection=connection;
	}

	/* ------------------------------------------------------------ */
	public void addListener(AsyncListener listener)
	{
		synchronized(this)
		{
			if (_asyncListeners==null)
				_asyncListeners=new ArrayList<AsyncListener>();
			_asyncListeners.add(listener);
		}
	}

	/* ------------------------------------------------------------ */
	public void addListener(AsyncListener listener,ServletRequest request, ServletResponse response)
	{
		synchronized(this)
		{
			// TODO handle the request/response ???
			if (_asyncListeners==null)
				_asyncListeners=new ArrayList<AsyncListener>();
			_asyncListeners.add(listener);
		}
	}

	/* ------------------------------------------------------------ */
	public void addContinuationListener(ContinuationListener listener)
	{
		synchronized(this)
		{
			if (_continuationListeners==null)
				_continuationListeners=new ArrayList<ContinuationListener>();
			_continuationListeners.add(listener);
		}
	}


	
	@Override
	public String toString()
	{
		synchronized (this)
		{
			return super.toString()+"@"+getStatusString();
		}
	}

	public String getStatusString()
	{
		synchronized (this)
		{
			return
			((_state==__IDLE)?"IDLE":
				(_state==__DISPATCHED)?"DISPATCHED":
											(_state==__UNCOMPLETED)?"UNCOMPLETED":
												(_state==__COMPLETED)?"COMPLETE":
													("UNKNOWN?"+_state));
		}
	}

	protected synchronized void handling()
	{
		switch(_state)
		{
			case __IDLE:
				_state=__DISPATCHED;
				if (_asyncListeners!=null)
					_asyncListeners.clear();
				return;
				
			default:
				throw new IllegalStateException(this.getStatusString());
		}
	}

	/* ------------------------------------------------------------ */
	/**
	 * 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 true if handling is complete, false if the request should 
	 * be handled again (eg because of a resume that happened before unhandle was called)
	 */
	protected synchronized void unhandle()
	{
		switch(_state)
		{
			case __DISPATCHED:
				_state = __UNCOMPLETED;
				return;

			default:
				throw new IllegalStateException(this.getStatusString());
		}
	}

		
	/* ------------------------------------------------------------ */
	/* (non-Javadoc)
	 * @see javax.servlet.ServletRequest#complete()
	 */
	protected void doComplete(Throwable ex)
	{
		final List<ContinuationListener> cListeners;
		final List<AsyncListener> aListeners;
		synchronized (this)
		{
			switch(_state)
			{
				case __UNCOMPLETED:
					_state = __COMPLETED;
					cListeners=_continuationListeners;
					aListeners=_asyncListeners;
					break;
					
				default:
					cListeners=null;
					aListeners=null;
					throw new IllegalStateException(this.getStatusString());
			}
		}
		
		if (aListeners!=null)
		{
			for (AsyncListener listener : aListeners)
			{
				try
				{
					if (ex!=null)
					{
						throw new UnsupportedOperationException();
					}
					else
						listener.onComplete(null);
				}
				catch(Exception e)
				{
					LOG.warn("",e);
				}
			}
		}
		if (cListeners!=null)
		{
			for (ContinuationListener listener : cListeners)
			{
				try
				{
					listener.onComplete(this);
				}
				catch(Exception e)
				{
					LOG.warn("",e);
				}
			}
		}
	}

	/* ------------------------------------------------------------ */
	protected void recycle()
	{
		synchronized (this)
		{
			switch(_state)
			{
				case __DISPATCHED:
					throw new IllegalStateException(getStatusString());
				default:
					_state=__IDLE;
			}
			cancelTimeout();
			_continuationListeners=null;
		}
	}    
	
	/* ------------------------------------------------------------ */
	protected void cancelTimeout()
	{
		EndPoint endp=_connection.getEndPoint();
		if (endp.isBlocking())
		{
			synchronized(this)
			{
				_expireAt=0;
				this.notifyAll();
			}
		}
		else 
		{
		}
	}

	synchronized boolean isUncompleted()
	{
		return _state==__UNCOMPLETED;
	} 
	
	public synchronized boolean isComplete()
	{
		return _state==__COMPLETED;
	}

	/* ------------------------------------------------------------ */
	public Request getBaseRequest()
	{
		return _connection.getRequest();
	}
	
	/* ------------------------------------------------------------ */
	public ServletRequest getRequest()
	{
		return _connection.getRequest();
	}

	/* ------------------------------------------------------------ */
	public ServletResponse getResponse()
	{
		return _connection.getResponse();
	}


	/* ------------------------------------------------------------ */
	/**
	 * @see org.eclipse.jetty.continuation.Continuation#getServletResponse()
	 */
	public ServletResponse getServletResponse()
	{
		return _connection.getResponse();
	}

	/* ------------------------------------------------------------ */
	/**
	 * @see org.eclipse.jetty.continuation.Continuation#getAttribute(java.lang.String)
	 */
	public Object getAttribute(String name)
	{
		return _connection.getRequest().getAttribute(name);
	}

	/* ------------------------------------------------------------ */
	/**
	 * @see org.eclipse.jetty.continuation.Continuation#removeAttribute(java.lang.String)
	 */
	public void removeAttribute(String name)
	{
		_connection.getRequest().removeAttribute(name);
	}

	/* ------------------------------------------------------------ */
	/**
	 * @see org.eclipse.jetty.continuation.Continuation#setAttribute(java.lang.String, java.lang.Object)
	 */
	public void setAttribute(String name, Object attribute)
	{
		_connection.getRequest().setAttribute(name,attribute);
	}

}