// ========================================================================
// $Id: SocketListener.java,v 1.39 2006/02/27 13:03:50 gregwilkins Exp $
// Copyright 199-2004 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ========================================================================
package net.lightbody.bmp.proxy.jetty.http;
import net.lightbody.bmp.proxy.jetty.log.LogFactory;
import net.lightbody.bmp.proxy.jetty.util.InetAddrPort;
import net.lightbody.bmp.proxy.jetty.util.LogSupport;
import net.lightbody.bmp.proxy.jetty.util.ThreadedServer;
import org.apache.commons.logging.Log;
import java.io.IOException;
import java.net.Socket;
/* ------------------------------------------------------------ */
/** Socket HTTP Listener.
* The behaviour of the listener can be controlled with the
* attributues of the ThreadedServer and ThreadPool from which it is
* derived. Specifically: <PRE>
* MinThreads - Minumum threads waiting to service requests.
* MaxThread - Maximum thread that will service requests.
* MaxIdleTimeMs - Time for an idle thread to wait for a request or read.
* LowResourcePersistTimeMs - time in ms that connections will persist if listener is
* low on resources.
* </PRE>
* @version $Id: SocketListener.java,v 1.39 2006/02/27 13:03:50 gregwilkins Exp $
* @author Greg Wilkins (gregw)
*/
public class SocketListener
extends ThreadedServer
implements HttpListener
{
private static Log log = LogFactory.getLog(SocketListener.class);
/* ------------------------------------------------------------------- */
private int _lowResourcePersistTimeMs=2000;
private String _scheme=HttpMessage.__SCHEME;
private String _integralScheme=HttpMessage.__SSL_SCHEME;
private String _confidentialScheme=HttpMessage.__SSL_SCHEME;
private int _integralPort=0;
private int _confidentialPort=0;
private boolean _identifyListener=false;
private int _bufferSize=8192;
private int _bufferReserve=512;
private HttpHandler _handler;
private int _lowResources;
private transient HttpServer _server;
private transient boolean _isLow=false;
private transient boolean _isOut=false;
private transient long _warned=0;
/* ------------------------------------------------------------------- */
public SocketListener()
{}
/* ------------------------------------------------------------------- */
public SocketListener(InetAddrPort address)
{
super(address);
}
/* ------------------------------------------------------------ */
public HttpServer getHttpServer()
{
return _server;
}
/* ------------------------------------------------------------ */
public void setHttpServer(HttpServer server)
{
if (server!=null && _server!=null && _server!=server)
throw new IllegalStateException("Cannot share listeners");
_server=server;
}
/* ------------------------------------------------------------ */
public HttpHandler getHttpHandler()
{
return _handler;
}
/* ------------------------------------------------------------ */
public void setHttpHandler(HttpHandler handler)
{
_handler=handler;
}
/* ------------------------------------------------------------ */
public int getBufferSize()
{
return _bufferSize;
}
/* ------------------------------------------------------------ */
public void setBufferSize(int size)
{
_bufferSize=size;
}
/* ------------------------------------------------------------ */
public int getBufferReserve()
{
return _bufferReserve;
}
/* ------------------------------------------------------------ */
public void setBufferReserve(int size)
{
_bufferReserve=size;
}
/* ------------------------------------------------------------ */
public boolean getIdentifyListener()
{
return _identifyListener;
}
/* ------------------------------------------------------------ */
/**
* @param identifyListener If true, the listener name is added to all
* requests as the org.mortbay.http.HttListener attribute
*/
public void setIdentifyListener(boolean identifyListener)
{
_identifyListener = identifyListener;
}
/* --------------------------------------------------------------- */
public void setDefaultScheme(String scheme)
{
_scheme=scheme;
}
/* --------------------------------------------------------------- */
public String getDefaultScheme()
{
return _scheme;
}
/* ------------------------------------------------------------ */
/**
* @return Returns the lowResources threshold
*/
public int getLowResources()
{
return _lowResources;
}
/* ------------------------------------------------------------ */
/**
* @param lowResources The number of idle threads needed to not be in
* low resources state.
*/
public void setLowResources(int lowResources)
{
_lowResources = lowResources;
}
/* ------------------------------------------------------------ */
/**
* @return time in ms that connections will persist if listener is
* low on resources.
*/
public int getLowResourcePersistTimeMs()
{
return _lowResourcePersistTimeMs;
}
/* ------------------------------------------------------------ */
/** Set the low resource persistace time.
* When the listener is low on resources, this timeout is used for idle
* persistent connections. It is desirable to have this set to a short
* period of time so that idle persistent connections do not consume
* resources on a busy server.
* @param ms time in ms that connections will persist if listener is
* low on resources.
*/
public void setLowResourcePersistTimeMs(int ms)
{
_lowResourcePersistTimeMs=ms;
}
/* --------------------------------------------------------------- */
public void start()
throws Exception
{
super.start();
log.info("Started SocketListener on "+getInetAddrPort());
}
/* --------------------------------------------------------------- */
public void stop()
throws InterruptedException
{
super.stop();
log.info("Stopped SocketListener on "+getInetAddrPort());
}
/* ------------------------------------------------------------ */
/** Handle Job.
* Implementation of ThreadPool.handle(), calls handleConnection.
* @param socket A Connection.
*/
public void handleConnection(Socket socket)
throws IOException
{
HttpConnection connection = createConnection(socket);
try
{
if (_lowResourcePersistTimeMs>0 && isLowOnResources())
{
socket.setSoTimeout(_lowResourcePersistTimeMs);
connection.setThrottled(true);
}
else
{
socket.setSoTimeout(getMaxIdleTimeMs());
connection.setThrottled(false);
}
}
catch(Exception e)
{
log.warn(LogSupport.EXCEPTION,e);
}
connection.handle();
}
/* ------------------------------------------------------------ */
/** Create an HttpConnection instance. This method can be used to
* override the connection instance.
* @param socket The underlying socket.
*/
protected HttpConnection createConnection(Socket socket)
throws IOException
{
HttpConnection c = new HttpConnection(this,
socket.getInetAddress(),
socket.getInputStream(),
socket.getOutputStream(),
socket);
return c;
}
/* ------------------------------------------------------------ */
/** Customize the request from connection.
* This method extracts the socket from the connection and calls
* the customizeRequest(Socket,HttpRequest) method.
* @param request
*/
public void customizeRequest(HttpConnection connection,
HttpRequest request)
{
if (_identifyListener)
request.setAttribute(HttpListener.ATTRIBUTE,getName());
Socket socket=(Socket)(connection.getConnection());
customizeRequest(socket,request);
}
/* ------------------------------------------------------------ */
/** Customize request from socket.
* Derived versions of SocketListener may specialize this method
* to customize the request with attributes of the socket used (eg
* SSL session ids).
* This version resets the SoTimeout if it has been reduced due to
* low resources. Derived implementations should call
* super.customizeRequest(socket,request) unless persistConnection
* has also been overridden and not called.
* @param request
*/
protected void customizeRequest(Socket socket,
HttpRequest request)
{
try
{
if (request.getHttpConnection().isThrottled())
{
socket.setSoTimeout(getMaxIdleTimeMs());
request.getHttpConnection().setThrottled(false);
}
}
catch(Exception e)
{
LogSupport.ignore(log,e);
}
}
/* ------------------------------------------------------------ */
/** Persist the connection.
* This method is called by the HttpConnection in order to prepare a
* connection to be persisted. For this implementation,
* if the listener is low on resources, the connection read
* timeout is set to lowResourcePersistTimeMs. The
* customizeRequest method is used to reset this to the normal
* value after a request has been read.
* @param connection The HttpConnection to use.
*/
public void persistConnection(HttpConnection connection)
{
try
{
Socket socket=(Socket)(connection.getConnection());
if (_lowResourcePersistTimeMs>0 && isLowOnResources())
{
socket.setSoTimeout(_lowResourcePersistTimeMs);
connection.setThrottled(true);
}
else
connection.setThrottled(false);
}
catch(Exception e)
{
LogSupport.ignore(log,e);
}
}
/* ------------------------------------------------------------ */
/** Get the lowOnResource state of the listener.
* A SocketListener is considered low on resources if the total number of
* the number of idle threads is less than the lowResource value (or minThreads if not set)
* @return True if low on idle threads.
*/
public boolean isLowOnResources()
{
int spare=getMaxThreads()-getThreads();
int lr = _lowResources>0?_lowResources:getMinThreads();
boolean low = (spare+getIdleThreads())<lr;
if (low && !_isLow)
{
log.info("LOW ON THREADS (("+
getMaxThreads()+"-"+
getThreads()+"+"+
getIdleThreads()+")<"+
getMinThreads()+") on "+ this);
_warned=System.currentTimeMillis();
_isLow=true;
}
else if (!low && _isLow)
{
if (System.currentTimeMillis()-_warned > 1000)
{
_isOut=false;
_isLow=false;
}
}
return low;
}
/* ------------------------------------------------------------ */
/** Get the outOfResource state of the listener.
* A SocketListener is considered out of resources if the total number of
* threads is maxThreads and the number of idle threads is zero.
* @return True if out of resources.
*/
public boolean isOutOfResources()
{
boolean out =
getThreads()==getMaxThreads() &&
getIdleThreads()==0;
if (out && !_isOut)
{
log.warn("OUT OF THREADS: "+this);
_warned=System.currentTimeMillis();
_isLow=true;
_isOut=true;
}
return out;
}
/* ------------------------------------------------------------ */
public boolean isIntegral(HttpConnection connection)
{
return false;
}
/* ------------------------------------------------------------ */
public boolean isConfidential(HttpConnection connection)
{
return false;
}
/* ------------------------------------------------------------ */
public String getIntegralScheme()
{
return _integralScheme;
}
/* ------------------------------------------------------------ */
public void setIntegralScheme(String integralScheme)
{
_integralScheme = integralScheme;
}
/* ------------------------------------------------------------ */
public int getIntegralPort()
{
return _integralPort;
}
/* ------------------------------------------------------------ */
public void setIntegralPort(int integralPort)
{
_integralPort = integralPort;
}
/* ------------------------------------------------------------ */
public String getConfidentialScheme()
{
return _confidentialScheme;
}
/* ------------------------------------------------------------ */
public void setConfidentialScheme(String confidentialScheme)
{
_confidentialScheme = confidentialScheme;
}
/* ------------------------------------------------------------ */
public int getConfidentialPort()
{
return _confidentialPort;
}
/* ------------------------------------------------------------ */
public void setConfidentialPort(int confidentialPort)
{
_confidentialPort = confidentialPort;
}
}