<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry excluding="org/apache/tomcat/lite/servlet/ServletApi30.java|org/apache/tomcat/lite/BodyReader.java|org/apache/tomcat/lite/BodyWriter.java|org/apache/tomcat/lite/ClientAbortException.java|org/apache/tomcat/lite/Connector.java|org/apache/tomcat/lite/ContextPreinitListener.java|org/apache/tomcat/lite/FilterChainImpl.java|org/apache/tomcat/lite/FilterConfigImpl.java|org/apache/tomcat/lite/Locale2Charset.java|org/apache/tomcat/lite/ParameterMap.java|org/apache/tomcat/lite/RequestDispatcherImpl.java|org/apache/tomcat/lite/ServletConfigImpl.java|org/apache/tomcat/lite/ServletContextImpl.java|org/apache/tomcat/lite/ServletInputStreamImpl.java|org/apache/tomcat/lite/ServletOutputStreamImpl.java|org/apache/tomcat/lite/ServletReaderImpl.java|org/apache/tomcat/lite/ServletRequestImpl.java|org/apache/tomcat/lite/ServletRequestWrapperImpl.java|org/apache/tomcat/lite/ServletResponseImpl.java|org/apache/tomcat/lite/ServletResponseIncludeWrapper.java|org/apache/tomcat/lite/ServletWriterImpl.java|org/apache/tomcat/lite/TomcatLite.java|org/apache/tomcat/lite/WebappContextMapper.java|org/apache/tomcat/lite/WebappFilterMapper.java|org/apache/tomcat/lite/WebappServletMapper.java|org/apache/tomcat/lite/webxml/|org/apache/tomcat/lite/coyote/CoyoteConnector.java|org/apache/coyote/servlet/" kind="src" path="java"/>
- <classpathentry excluding="org/apache/coyote/lite/TomcatLiteCoyoteTest.java|org/apache/coyote/servlet/" kind="src" path="test"/>
+ <classpathentry excluding="org/apache/coyote/servlet/" kind="src" path="test"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/>
<classpathentry kind="con" path="org.apache.ivyde.eclipse.cpcontainer.IVYDE_CONTAINER/?ivyXmlPath=pom.xml&confs=compile"/>
import java.io.IOException;
import java.util.Iterator;
+import org.apache.coyote.ActionCode;
+import org.apache.coyote.ActionHook;
import org.apache.coyote.Adapter;
import org.apache.coyote.InputBuffer;
import org.apache.coyote.OutputBuffer;
import org.apache.tomcat.lite.http.HttpConnector;
import org.apache.tomcat.lite.http.HttpRequest;
import org.apache.tomcat.lite.http.HttpResponse;
+import org.apache.tomcat.lite.http.MultiMap;
import org.apache.tomcat.lite.http.HttpChannel.HttpService;
+import org.apache.tomcat.lite.http.MultiMap.Entry;
import org.apache.tomcat.lite.io.CBuffer;
import org.apache.tomcat.lite.io.SocketConnector;
import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.buf.MessageBytes;
+import org.apache.tomcat.util.http.MimeHeaders;
/**
* Work in progress - use the refactored http as a coyote connector.
buffer.length());
}
- private void coyoteService(HttpRequest httpReq, final HttpResponse httpRes) {
- Request req = new Request();
- req.setInputBuffer(new InputBuffer() {
- @Override
- public int doRead(ByteChunk chunk, Request request)
- throws IOException {
- // TODO
- return 0;
- }
- });
- Response res = new Response();
- res.setOutputBuffer(new OutputBuffer() {
-
+ private void coyoteService(final HttpRequest httpReq, final HttpResponse httpRes) {
+ RequestData rc = new RequestData();
+ rc.init(httpReq, httpRes);
+
+ try {
+ adapter.service(rc.req, rc.res);
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Per request data.
+ */
+ public class RequestData implements ActionHook {
+ private final class LiteOutputBuffer implements OutputBuffer {
@Override
public int doWrite(org.apache.tomcat.util.buf.ByteChunk chunk,
Response response) throws IOException {
chunk.getLength());
return chunk.getLength();
}
-
- });
-
- // TODO: turn http request into a coyote request - copy all fields,
- // add hooks where needed.
+ }
+
+ OutputBuffer outputBuffer = new LiteOutputBuffer();
+ // TODO: recycle, etc.
+ Request req = new Request();
+
+ Response res = new Response();
+ HttpResponse httpRes;
+ HttpRequest httpReq;
+ InputBuffer inputBuffer = new InputBuffer() {
+ @Override
+ public int doRead(ByteChunk bchunk, Request request)
+ throws IOException {
+ httpReq.getBody().waitData(httpReq.getHttpChannel().getIOTimeout());
+ int rd =
+ httpReq.getBody().read(bchunk.getBytes(),
+ bchunk.getStart(), bchunk.getBytes().length);
+ if (rd > 0) {
+ bchunk.setEnd(bchunk.getEnd() + rd);
+ }
+ return rd;
+ }
+ };
- wrap(req.decodedURI(), httpReq.decodedURI());
- wrap(req.method(), httpReq.method());
- wrap(req.protocol(), httpReq.protocol());
- wrap(req.requestURI(), httpReq.requestURI());
- // Same for response.
+ public RequestData() {
+ req.setInputBuffer(inputBuffer);
+ res.setOutputBuffer(outputBuffer);
+ req.setResponse(res);
+ res.setRequest(req);
+ res.setHook(this);
+ }
- try {
+ public void init(HttpRequest httpReq, HttpResponse httpRes) {
+ this.httpRes = httpRes;
+ this.httpReq = httpReq;
+ // TODO: turn http request into a coyote request - copy all fields,
+ // add hooks where needed.
- adapter.service(req, res);
+ wrap(req.decodedURI(), httpReq.decodedURI());
+ wrap(req.method(), httpReq.method());
+ wrap(req.protocol(), httpReq.protocol());
+ wrap(req.requestURI(), httpReq.requestURI());
+ wrap(req.queryString(), httpReq.queryString());
+
+ req.setServerPort(httpReq.getServerPort());
+ req.serverName().setString(req.localName().toString());
- } catch (Exception e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
+ MultiMap mimeHeaders = httpReq.getMimeHeaders();
+ MimeHeaders coyoteHeaders = req.getMimeHeaders();
+ for (int i = 0; i < mimeHeaders.size(); i++ ) {
+ Entry entry = mimeHeaders.getEntry(i);
+ MessageBytes val =
+ coyoteHeaders.addValue(entry.getName().toString());
+ val.setString(entry.getValue().toString());
+ }
}
+
+ /**
+ * Send an action to the connector.
+ *
+ * @param actionCode Type of the action
+ * @param param Action parameter
+ */
+ public void action(ActionCode actionCode, Object param) {
+
+ if (actionCode == ActionCode.ACTION_COMMIT) {
+ if (res.isCommitted())
+ return;
+
+ // TODO: copy headers, fields
+ httpRes.setStatus(res.getStatus());
+ httpRes.setMessage(res.getMessage());
+ MultiMap mimeHeaders = httpRes.getMimeHeaders();
+ MimeHeaders coyoteHeaders = res.getMimeHeaders();
+ for (int i = 0; i < coyoteHeaders.size(); i++ ) {
+ MessageBytes name = coyoteHeaders.getName(i);
+ MessageBytes val = coyoteHeaders.getValue(i);
+ Entry entry = mimeHeaders.addEntry(name.toString());
+ entry.getValue().set(val.toString());
+ }
+ String contentType = res.getContentType();
+ if (contentType != null) {
+ mimeHeaders.addEntry("Content-Type").getValue().set(contentType);
+ }
+ String contentLang = res.getContentType();
+ if (contentLang != null) {
+ mimeHeaders.addEntry("Content-Language").getValue().set(contentLang);
+ }
+ long contentLength = res.getContentLengthLong();
+ if (contentLength != -1) {
+ httpRes.setContentLength(contentLength);
+ }
+ String lang = res.getContentLanguage();
+ if (lang != null) {
+ httpRes.setHeader("Content-Language", lang);
+ }
+
+ try {
+ httpReq.send();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ } else if (actionCode == ActionCode.ACTION_ACK) {
+ // Done automatically by http connector
+ } else if (actionCode == ActionCode.ACTION_CLIENT_FLUSH) {
+ try {
+ httpReq.send();
+ } catch (IOException e) {
+ httpReq.getHttpChannel().abort(e);
+ res.setErrorException(e);
+ }
+
+ } else if (actionCode == ActionCode.ACTION_CLOSE) {
+ // Close
+
+ // End the processing of the current request, and stop any further
+ // transactions with the client
+
+// comet = false;
+// try {
+// outputBuffer.endRequest();
+// } catch (IOException e) {
+// // Set error flag
+// error = true;
+// }
+
+ } else if (actionCode == ActionCode.ACTION_RESET) {
+ // Reset response
+ // Note: This must be called before the response is committed
+ httpRes.getBody().clear();
+
+ } else if (actionCode == ActionCode.ACTION_CUSTOM) {
+
+ // Do nothing
+
+ } else if (actionCode == ActionCode.ACTION_REQ_HOST_ADDR_ATTRIBUTE) {
+ req.remoteAddr().setString(httpReq.remoteAddr().toString());
+ } else if (actionCode == ActionCode.ACTION_REQ_LOCAL_NAME_ATTRIBUTE) {
+ req.localName().setString(httpReq.localName().toString());
+ } else if (actionCode == ActionCode.ACTION_REQ_HOST_ATTRIBUTE) {
+ req.remoteHost().setString(httpReq.remoteHost().toString());
+ } else if (actionCode == ActionCode.ACTION_REQ_LOCAL_ADDR_ATTRIBUTE) {
+ req.localAddr().setString(httpReq.localAddr().toString());
+ } else if (actionCode == ActionCode.ACTION_REQ_REMOTEPORT_ATTRIBUTE) {
+ req.setRemotePort(httpReq.getRemotePort());
+ } else if (actionCode == ActionCode.ACTION_REQ_LOCALPORT_ATTRIBUTE) {
+ req.setLocalPort(httpReq.getLocalPort());
+ } else if (actionCode == ActionCode.ACTION_REQ_SSL_ATTRIBUTE ) {
+
+// if (ssl && (socket != 0)) {
+// try {
+// // Cipher suite
+// Object sslO = SSLSocket.getInfoS(socket, SSL.SSL_INFO_CIPHER);
+// if (sslO != null) {
+// request.setAttribute(AprEndpoint.CIPHER_SUITE_KEY, sslO);
+// }
+// // Get client certificate and the certificate chain if present
+// // certLength == -1 indicates an error
+// int certLength = SSLSocket.getInfoI(socket, SSL.SSL_INFO_CLIENT_CERT_CHAIN);
+// byte[] clientCert = SSLSocket.getInfoB(socket, SSL.SSL_INFO_CLIENT_CERT);
+// X509Certificate[] certs = null;
+// if (clientCert != null && certLength > -1) {
+// certs = new X509Certificate[certLength + 1];
+// CertificateFactory cf = CertificateFactory.getInstance("X.509");
+// certs[0] = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(clientCert));
+// for (int i = 0; i < certLength; i++) {
+// byte[] data = SSLSocket.getInfoB(socket, SSL.SSL_INFO_CLIENT_CERT_CHAIN + i);
+// certs[i+1] = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(data));
+// }
+// }
+// if (certs != null) {
+// request.setAttribute(AprEndpoint.CERTIFICATE_KEY, certs);
+// }
+// // User key size
+// sslO = new Integer(SSLSocket.getInfoI(socket, SSL.SSL_INFO_CIPHER_USEKEYSIZE));
+// request.setAttribute(AprEndpoint.KEY_SIZE_KEY, sslO);
+//
+// // SSL session ID
+// sslO = SSLSocket.getInfoS(socket, SSL.SSL_INFO_SESSION_ID);
+// if (sslO != null) {
+// request.setAttribute(AprEndpoint.SESSION_ID_KEY, sslO);
+// }
+// //TODO provide a hook to enable the SSL session to be
+// // invalidated. Set AprEndpoint.SESSION_MGR req attr
+// } catch (Exception e) {
+// log.warn(sm.getString("http11processor.socket.ssl"), e);
+// }
+// }
+
+ } else if (actionCode == ActionCode.ACTION_REQ_SSL_CERTIFICATE) {
+
+// if (ssl && (socket != 0)) {
+// // Consume and buffer the request body, so that it does not
+// // interfere with the client's handshake messages
+// InputFilter[] inputFilters = inputBuffer.getFilters();
+// ((BufferedInputFilter) inputFilters[Constants.BUFFERED_FILTER]).setLimit(maxSavePostSize);
+// inputBuffer.addActiveFilter(inputFilters[Constants.BUFFERED_FILTER]);
+// try {
+// // Configure connection to require a certificate
+// SSLSocket.setVerify(socket, SSL.SSL_CVERIFY_REQUIRE,
+// endpoint.getSSLVerifyDepth());
+// // Renegotiate certificates
+// if (SSLSocket.renegotiate(socket) == 0) {
+// // Don't look for certs unless we know renegotiation worked.
+// // Get client certificate and the certificate chain if present
+// // certLength == -1 indicates an error
+// int certLength = SSLSocket.getInfoI(socket,SSL.SSL_INFO_CLIENT_CERT_CHAIN);
+// byte[] clientCert = SSLSocket.getInfoB(socket, SSL.SSL_INFO_CLIENT_CERT);
+// X509Certificate[] certs = null;
+// if (clientCert != null && certLength > -1) {
+// certs = new X509Certificate[certLength + 1];
+// CertificateFactory cf = CertificateFactory.getInstance("X.509");
+// certs[0] = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(clientCert));
+// for (int i = 0; i < certLength; i++) {
+// byte[] data = SSLSocket.getInfoB(socket, SSL.SSL_INFO_CLIENT_CERT_CHAIN + i);
+// certs[i+1] = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(data));
+// }
+// }
+// if (certs != null) {
+// request.setAttribute(AprEndpoint.CERTIFICATE_KEY, certs);
+// }
+// }
+// } catch (Exception e) {
+// log.warn(sm.getString("http11processor.socket.ssl"), e);
+// }
+// }
+
+ } else if (actionCode == ActionCode.ACTION_REQ_SET_BODY_REPLAY) {
+// ByteChunk body = (ByteChunk) param;
+//
+// InputFilter savedBody = new SavedRequestInputFilter(body);
+// savedBody.setRequest(request);
+//
+// InternalAprInputBuffer internalBuffer = (InternalAprInputBuffer)
+// request.getInputBuffer();
+// internalBuffer.addActiveFilter(savedBody);
+
+ } else if (actionCode == ActionCode.ACTION_AVAILABLE) {
+ req.setAvailable(httpReq.getBody().available());
+ } else if (actionCode == ActionCode.ACTION_COMET_BEGIN) {
+// comet = true;
+ } else if (actionCode == ActionCode.ACTION_COMET_END) {
+// comet = false;
+ } else if (actionCode == ActionCode.ACTION_COMET_CLOSE) {
+ //no op
+ } else if (actionCode == ActionCode.ACTION_COMET_SETTIMEOUT) {
+ //no op
+// } else if (actionCode == ActionCode.ACTION_ASYNC_START) {
+// //TODO SERVLET3 - async
+// } else if (actionCode == ActionCode.ACTION_ASYNC_COMPLETE) {
+// //TODO SERVLET3 - async
+// } else if (actionCode == ActionCode.ACTION_ASYNC_SETTIMEOUT) {
+// //TODO SERVLET3 - async
+ }
+
+
+ }
+
}
-
-
-
}
import org.apache.tomcat.lite.io.Hex;
import org.apache.tomcat.lite.io.IOBuffer;
import org.apache.tomcat.lite.io.IOChannel;
+import org.apache.tomcat.lite.io.IOConnector;
+import org.apache.tomcat.lite.io.NioThread;
+import org.apache.tomcat.lite.io.SocketIOChannel;
public class Http11Connection extends HttpConnection {
public static final String CHUNKED = "chunked";
protected boolean http09 = false;
HttpConnection switchedProtocol = null;
+
+ private int requestCount = 0;
+
+ private Object readLock = new Object();
public Http11Connection(HttpConnector httpConnector) {
this.httpConnector = httpConnector;
private boolean readHead() throws IOException {
while (true) {
int read;
- if (headRecvBuf.remaining() < 4) {
+ if (requestCount == 0 && headRecvBuf.remaining() < 4) {
// requests have at least 4 bytes - detect protocol
read = net.getIn().read(headRecvBuf, 4);
if (read < 0) {
switchedProtocol.handleReceived(netx);
return;
}
-
+ //trace("handleReceived " + headersReceived);
if (!checkKeepAliveClient()) {
return; // we were in client keep alive mode
}
-
- if (!headersReceived) {
- if (!readHead()) {
- return;
+ // endSendReceived uses same lock - it will call this
+ // to check outstanding bytes
+ synchronized (readLock) {
+ if (bodyReceived) {
+ return; // leave data in net buffer, for next req
}
- }
-
- // We have a header
- if (activeHttp == null) {
- if (checkHttpChannel() == null) {
- return;
+
+ if (!headersReceived) {
+ if (!readHead()) {
+ return;
+ }
}
- }
-
- IOBuffer receiveBody = activeHttp.receiveBody;
- if (!headersReceived) {
- headRecvBuf.wrapTo(headW);
- parseMessage(activeHttp, headW);
- if (serverMode && activeHttp.httpReq.decodedUri.remaining() == 0) {
- abort(activeHttp, "Invalid url");
+ // We have a header
+ if (activeHttp == null) {
+ if (checkHttpChannel() == null) {
+ return;
+ }
}
- headersReceived = true;
- // Send header callbacks - we process any incoming data
- // first, so callbacks have more info
- activeHttp.handleHeadersReceived(activeHttp.inMessage);
- }
-
- // any remaining data will be processed as part of the
- // body - or left in the channel until endSendReceive()
-
- if (!bodyReceived) {
- // Will close receiveBody when it consummed enough
- rawDataReceived(activeHttp, receiveBody, net.getIn());
- // Did we process anything ?
- if (receiveBody.getBufferCount() > 0) {
- activeHttp.sendHandleReceivedCallback(); // callback
+ IOBuffer receiveBody = activeHttp.receiveBody;
+
+ if (!headersReceived) {
+ headRecvBuf.wrapTo(headW);
+ parseMessage(activeHttp, headW);
+ if (serverMode && activeHttp.httpReq.decodedUri.remaining() == 0) {
+ abort(activeHttp, "Invalid url");
+ }
+
+ headersReceived = true;
+ // Send header callbacks - we process any incoming data
+ // first, so callbacks have more info
+ trace("Send headers received callback " + activeHttp.httpService);
+ activeHttp.handleHeadersReceived(activeHttp.inMessage);
}
+ // any remaining data will be processed as part of the
+ // body - or left in the channel until endSendReceive()
+
+ if (!bodyReceived) {
+ // Will close receiveBody when it consummed enough
+ rawDataReceived(activeHttp, receiveBody, net.getIn());
+ // Did we process anything ?
+ if (receiveBody.getBufferCount() > 0) {
+ activeHttp.sendHandleReceivedCallback(); // callback
+ }
+ }
// Receive has marked the body as closed
if (receiveBody.isAppendClosed()) {
- activeHttp.handleEndReceive();
bodyReceived = true;
+ activeHttp.handleEndReceive();
}
- }
- if (net.getIn().isClosedAndEmpty()) {
- // If not already closed.
- closeStreamOnEnd("closed after body");
- }
+ if (net.getIn().isClosedAndEmpty()) {
+ // If not already closed.
+ closeStreamOnEnd("closed after body");
+ }
+ }
}
/**
switchedProtocol.endSendReceive(http);
return;
}
-
activeHttp = null;
if (!keepAlive()) {
if (debug) {
}
if (net != null) {
net.close();
-// net.getOut().close(); // shutdown output if not done
-// net.getIn().close(); // this should close the socket
net.startSending();
-
+
}
beforeRequest();
return;
}
-
- beforeRequest(); // will clear head buffer
-
- if (serverMode) {
- handleReceived(net); // will attempt to read next req
- if (debug) {
- log.info(">>> server socket KEEP_ALIVE " + net.getTarget() +
- " " + net);
- }
-
- } else {
- if (debug) {
- log.info(">>> client socket KEEP_ALIVE " + net.getTarget() +
- " " + net);
+ synchronized (readLock) {
+ beforeRequest(); // will clear head buffer
+ requestCount++;
+ if (serverMode) {
+ if (debug) {
+ log.info(">>> server socket KEEP_ALIVE " + net.getTarget() +
+ " " + net + " " + net.getIn().available());
+ }
+ handleReceived(net); // will attempt to read next req
+ } else {
+ if (debug) {
+ log.info(">>> client socket KEEP_ALIVE " + net.getTarget() +
+ " " + net);
+ }
+ httpConnector.cpool.returnChannel(this);
}
- httpConnector.cpool.returnChannel(this);
}
+
}
private void trace(String s) {
return switchedProtocol.toString();
}
- return (serverMode ? "S11 " : "C11 ") +
- (keepAlive() ? " KA " : "");
+ return (serverMode ? "SR " : "CL ") +
+ (keepAlive() ? " KA " : "") +
+ (headersReceived ? " HEAD " : "") +
+ (bodyReceived ? " BODY " : "")
+ ;
}
}
*
* @throws IOException
*/
- public void abort(Throwable t) throws IOException {
+ public void abort(Throwable t) {
abort(t.toString());
}
- public void abort(String t) throws IOException {
+ public void abort(String t) {
synchronized (this) {
if (abortDone) {
return;
}
abortDone = true;
}
-
- checkRelease();
- trace("abort " + t);
- log.info("Abort connection " + t);
- if (conn != null) {
- conn.abort(this, t);
+ try {
+ checkRelease();
+ trace("abort " + t);
+ log.info("Abort connection " + t);
+ if (conn != null) {
+ conn.abort(this, t);
+ }
+ inMessage.state = HttpMessage.State.DONE;
+ outMessage.state = HttpMessage.State.DONE;
+ sendReceiveDone = true;
+ error = true;
+ handleEndSendReceive();
+ } catch (Throwable ex) {
+ log.severe("Exception in abort " + ex);
}
- inMessage.state = HttpMessage.State.DONE;
- outMessage.state = HttpMessage.State.DONE;
- sendReceiveDone = true;
- error = true;
- handleEndSendReceive();
}
/**
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
-import java.util.Queue;
import java.util.Set;
import java.util.Timer;
-import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Logger;
HttpConnectionManager conManager = new HttpConnectionManager();
private static Logger log = Logger.getLogger("HttpConnector");
- private int maxHttpPoolSize = 20;
- private int maxSocketPoolSize = 100;
- private int keepAliveTimeMs = 30000;
+ /**
+ * Cache HttpChannel/request/buffers
+ */
+ private int maxHttpPoolSize = 50;
+
+ /**
+ * Max number of connections to keep alive.
+ * Each connection holds a header buffer and the socket.
+ * ( we could skip the header buffer )
+ */
+ private int maxSocketPoolSize = 500; // 10000;
+
+ private int keepAliveTimeMs = 300000;
- private Queue<HttpChannel> httpChannelPool = new ConcurrentLinkedQueue<HttpChannel>();
+ private List<HttpChannel> httpChannelPool = new ArrayList<HttpChannel>();
protected IOConnector ioConnector;
Dispatcher dispatcher;
protected HttpService defaultService;
int port = 8080;
-
+
+ private Timer timer;
+
+ private static Timer defaultTimer = new Timer(true);
public HttpConnector(IOConnector ioConnector) {
this.ioConnector = ioConnector;
dispatcher = new Dispatcher();
defaultService = dispatcher;
+ if (ioConnector != null) {
+ timer = ioConnector.getTimer();
+ } else {
+ // tests
+ timer = defaultTimer;
+ }
}
protected HttpConnector() {
protected HttpChannel get(boolean server) throws IOException {
HttpChannel processor = null;
synchronized (httpChannelPool) {
- processor = httpChannelPool.poll();
+ int cnt = httpChannelPool.size();
+ if (cnt > 0) {
+ processor = httpChannelPool.remove(cnt - 1);
+ }
}
boolean reuse = false;
totalHttpChannel.incrementAndGet();
+ if (!server) {
+ totalClientHttpChannel.incrementAndGet();
+ }
if (processor == null) {
processor = create();
} else {
reusedChannels.incrementAndGet();
processor.release = false;
}
- if (!server) {
- totalClientHttpChannel.incrementAndGet();
- }
processor.serverMode(server);
if (debug) {
log.info((reuse ? "REUSE ": "Create ") +
return processor;
}
-
- /**
- * Called by HttpChannel when the HTTP request is done, i.e. all
- * sending/receiving is complete. The service may still use the
- * HttpChannel object.
- *
- * If keepOpen: clients will wait in the pool, detecting server close.
- * For server: will wait for new requests.
- *
- * TODO: timeouts, better pool management
- */
- protected void returnSocket(IOChannel ch, boolean serverMode,
- boolean keepOpen)
- throws IOException {
- // Now handle net - note that we could have reused the async object
-
-
- }
-
protected void returnToPool(HttpChannel http) throws IOException {
inUse.decrementAndGet();
recycledChannels.incrementAndGet();
- if (debug) {
- log.info("Return " + http.getTarget() + " obj=" +
- http + " size=" + httpChannelPool.size());
- }
+ int size = 0;
+ boolean pool = false;
http.recycle();
+ http.setConnection(null);
+ http.setConnector(null);
// No more data - release the object
synchronized (httpChannelPool) {
- http.setConnection(null);
- http.setConnector(null);
+ size = httpChannelPool.size();
if (httpChannelPool.contains(http)) {
- System.err.println("dup ? ");
- }
- if (httpChannelPool.size() >= maxHttpPoolSize) {
- if (httpEvents != null) {
- httpEvents.onDestroy(http, this);
- }
- } else {
+ log.severe("Duplicate element in pool !");
+ } else if (size < maxHttpPoolSize) {
httpChannelPool.add(http);
+ pool = true;
}
}
+
+ if (!pool && httpEvents != null) {
+ httpEvents.onDestroy(http, this);
+ }
+ if (debug) {
+ log.info((pool ? "Return " : "Destroy ")
+ + http.getTarget() + " obj=" +
+ http + " size=" + size);
+ }
}
implements DataReceivedCallback
{
protected HttpConnector httpConnector;
- protected boolean serverMode;
+ protected boolean serverMode = false;
protected BBuffer headRecvBuf = BBuffer.allocate(8192);
*/
public Map<CharSequence, RemoteServer> hosts = new HashMap<CharSequence,
RemoteServer>();
- boolean keepOpen = true;
// Statistics
public AtomicInteger waitingSockets = new AtomicInteger();
public AtomicInteger hits = new AtomicInteger();
public AtomicInteger misses = new AtomicInteger();
- Timer timer;
-
public int getTargetCount() {
return hosts.size();
}
return;
}
- if (!keepOpen) {
- ch.close();
- return;
- }
-
-// SocketIOChannel sdata = (SocketIOChannel) ch;
if (!ch.isOpen()) {
ch.close(); // make sure all closed
if (debug) {
}
public int addHeader() {
- headerNames.add(BBuffer.wrapper());
- headerValues.add(BBuffer.wrapper());
+ if (headerCount >= headerNames.size()) {
+ // make space for the new header.
+ headerNames.add(BBuffer.wrapper());
+ headerValues.add(BBuffer.wrapper());
+ }
return headerCount++;
}
package org.apache.tomcat.lite.io;
import java.io.IOException;
+import java.util.Timer;
/**
public static interface DataFlushedCallback {
public void handleFlushed(IOChannel ch) throws IOException;
}
+
+ protected Timer timer;
+
+ public Timer getTimer() {
+ return timer;
+ }
public abstract void acceptor(IOConnector.ConnectedCallback sc,
CharSequence port, Object extra)
throws IOException;
-
+
// TODO: failures ?
// TODO: use String target or url
public abstract void connect(String host, int port,
IOConnector.ConnectedCallback sc) throws IOException;
public void stop() {
-
+ if (timer != null) {
+ timer.cancel();
+ }
}
}
package org.apache.tomcat.lite.io;
import java.io.IOException;
+import java.util.Timer;
public class MemoryIOConnector extends IOConnector {
ConnectedCallback acceptor;
MemoryIOConnector server;
+ public MemoryIOConnector() {
+ timer = new Timer(true);
+ }
+
public MemoryIOConnector withServer(MemoryIOConnector server) {
this.server = server;
return server;
// handle events for existing req first.
if (selected != 0) {
sloops = 0;
+ int callbackCnt = 0;
Set<SelectionKey> sel = selector.selectedKeys();
Iterator<SelectionKey> i = sel.iterator();
while (i.hasNext()) {
+ callbackCnt++;
+ long beforeCallback = System.currentTimeMillis();
SelectionKey sk = i.next();
i.remove();
}
long callbackTime =
- System.currentTimeMillis() - lastWakeup;
+ System.currentTimeMillis() - beforeCallback;
if (callbackTime > 250) {
log.warning("Callback too long ! ops=" + ready +
- " time=" + callbackTime + " ch=" + ch);
+ " time=" + callbackTime + " ch=" + ch +
+ " " + callbackCnt);
}
if (callbackTime > maxCallbackTime) {
maxCallbackTime = callbackTime;
selector = Selector.open();
for (int i = 0; i < oldCh.size(); i++) {
NioChannel selectorData = oldCh.get(i);
+ if (selectorData == null) {
+ continue;
+ }
int interest = interests.get(i);
if (selectorData.channel instanceof ServerSocketChannel) {
ServerSocketChannel socketChannel =
return Thread.currentThread() == selectorThread;
}
+ public static boolean isSelectorThread(IOChannel ch) {
+ SocketIOChannel sc = (SocketIOChannel) ch.getFirst();
+ return Thread.currentThread() == sc.ch.sel.selectorThread;
+ }
+
}
\ No newline at end of file
import java.io.IOException;
import java.net.InetSocketAddress;
+import java.util.Timer;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.logging.Logger;
Executor threadPool = Executors.newCachedThreadPool();
public SocketConnector() {
+ timer = new Timer(true);
}
/**
private ConnectionPool pool;
- public IOStatus() {
-
- }
-
public IOStatus(ConnectionPool pool) {
this.pool = pool;
}
@Override
public void service(HttpRequest httpReq, HttpResponse httpRes)
throws IOException {
- ConnectionPool sc = pool == null ?
- httpReq.getHttpChannel().getConnector().cpool :
- pool;
+ ConnectionPool sc = pool;
HttpWriter out = httpRes.getBodyWriter();
httpRes.setContentType("text/plain");
</excludes>
<testExcludes>
<exclude>org/apache/coyote/servlet/**</exclude>
- <exclude>org/apache/coyote/lite/**</exclude>
<exclude>**/ServletApi30.java</exclude>
</testExcludes>
</configuration>
--- /dev/null
+/*
+ */
+package org.apache.coyote.lite;
+
+import java.io.File;
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+
+import org.apache.catalina.LifecycleException;
+import org.apache.tomcat.lite.servlet.TomcatLite;
+import org.apache.tomcat.lite.servlet.TomcatLiteWatchdog;
+
+import junit.framework.Test;
+
+public class ServletTests extends TomcatLiteWatchdog {
+
+ public ServletTests() {
+ super();
+ port = 7075;
+ exclude = new String[] {
+ "ServletToJSPErrorPageTest",
+ "ServletToJSPError502PageTest",
+ };
+ }
+
+ public ServletTests(String name) {
+ super(name);
+ port = 7075;
+ }
+
+ protected void addConnector(TomcatLite connector) {
+
+ }
+
+ public void initServerWithWatchdog(String wdDir) throws ServletException,
+ IOException {
+ Tomcat tomcat = new Tomcat();
+
+ File f = new File(wdDir + "/build/webapps");
+ tomcat.setPort(port);
+ tomcat.setBaseDir(f.getCanonicalPath());
+
+ TomcatLiteCoyoteTest.setUp(tomcat, port);
+
+ for (String s : new String[] {
+ "servlet-compat",
+ "servlet-tests",
+ "jsp-tests"} ) {
+ tomcat.addWebapp("/" + s, f.getCanonicalPath() + "/" + s);
+ }
+
+ try {
+ tomcat.start();
+ } catch (LifecycleException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Magic JUnit method
+ */
+ public static Test suite() {
+ return new ServletTests().getSuite();
+ }
+}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.coyote.lite;
+
+import java.io.File;
+import java.io.IOException;
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.servlet.Servlet;
+import javax.servlet.ServletException;
+
+import org.apache.catalina.Container;
+import org.apache.catalina.Context;
+import org.apache.catalina.Lifecycle;
+import org.apache.catalina.LifecycleEvent;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.LifecycleListener;
+import org.apache.catalina.Realm;
+import org.apache.catalina.connector.Connector;
+import org.apache.catalina.core.StandardContext;
+import org.apache.catalina.core.StandardEngine;
+import org.apache.catalina.core.StandardHost;
+import org.apache.catalina.core.StandardServer;
+import org.apache.catalina.core.StandardService;
+import org.apache.catalina.core.StandardWrapper;
+import org.apache.catalina.realm.GenericPrincipal;
+import org.apache.catalina.realm.RealmBase;
+import org.apache.catalina.session.StandardManager;
+import org.apache.catalina.startup.ContextConfig;
+
+// TODO: lazy init for the temp dir - only when a JSP is compiled or
+// get temp dir is called we need to create it. This will avoid the
+// need for the baseDir
+
+// TODO: allow contexts without a base dir - i.e.
+// only programmatic. This would disable the default servlet.
+
+/**
+ * Minimal tomcat starter for embedding/unit tests.
+ *
+ * Tomcat supports multiple styles of configuration and
+ * startup - the most common and stable is server.xml-based,
+ * implemented in org.apache.catalina.startup.Bootstrap.
+ *
+ * This class is for use in apps that embed tomcat.
+ * Requirements:
+ *
+ * - all tomcat classes and possibly servlets are in the classpath.
+ * ( for example all is in one big jar, or in eclipse CP, or in any other
+ * combination )
+ *
+ * - we need one temporary directory for work files
+ *
+ * - no config file is required. This class provides methods to
+ * use if you have a webapp with a web.xml file, but it is
+ * optional - you can use your own servlets.
+ *
+ * This class provides a main() and few simple CLI arguments,
+ * see setters for doc. It can be used for simple tests and
+ * demo.
+ *
+ * @see TestTomcat for examples on how to use this
+ * @author Costin Manolache
+ */
+public class Tomcat {
+ // Single engine, service, server, connector - few cases need more,
+ // they can use server.xml
+ protected StandardServer server;
+ protected StandardService service;
+ protected StandardEngine engine;
+ protected Connector connector; // for more - customize the classes
+
+ // To make it a bit easier to config for the common case
+ // ( one host, one context ).
+ protected StandardHost host;
+
+ // TODO: it's easy to add support for more hosts - but is it
+ // really needed ?
+
+ // TODO: allow use of in-memory connector
+
+ protected int port = 8080;
+ protected String hostname = "localhost";
+ protected String basedir;
+
+ // Default in-memory realm, will be set by default on
+ // created contexts. Can be replaced with setRealm() on
+ // the context.
+ protected Realm defaultRealm;
+ private Map<String, String> userPass = new HashMap<String, String>();
+ private Map<String, List<String>> userRoles =
+ new HashMap<String, List<String>>();
+ private Map<String, Principal> userPrincipals = new HashMap<String, Principal>();
+
+ public Tomcat() {
+ // NOOP
+ }
+
+ /**
+ * Tomcat needs a directory for temp files. This should be the
+ * first method called.
+ *
+ * By default, if this method is not called, we use:
+ * - system properties - catalina.base, catalina.home
+ * - $HOME/tomcat.$PORT
+ * ( /tmp doesn't seem a good choice for security ).
+ *
+ *
+ * TODO: better default ? Maybe current dir ?
+ * TODO: disable work dir if not needed ( no jsp, etc ).
+ */
+ public void setBaseDir(String basedir) {
+ this.basedir = basedir;
+ }
+
+ /**
+ * Set the port for the default connector. Must
+ * be called before start().
+ */
+ public void setPort(int port) {
+ this.port = port;
+ }
+
+ /**
+ * The the hostname of the default host, default is
+ * 'localhost'.
+ */
+ public void setHostname(String s) {
+ hostname = s;
+ }
+
+ /**
+ * Add a webapp using normal WEB-INF/web.xml if found.
+ *
+ * @param contextPath
+ * @param baseDir
+ * @return new StandardContext
+ * @throws ServletException
+ */
+ public StandardContext addWebapp(String contextPath,
+ String baseDir) throws ServletException {
+
+ return addWebapp(getHost(), contextPath, baseDir);
+ }
+
+
+ /**
+ * Add a context - programmatic mode, no web.xml used.
+ *
+ * API calls equivalent with web.xml:
+ *
+ * context-param
+ * ctx.addParameter("name", "value");
+ *
+ *
+ * error-page
+ * ErrorPage ep = new ErrorPage();
+ * ep.setErrorCode(500);
+ * ep.setLocation("/error.html");
+ * ctx.addErrorPage(ep);
+ *
+ * ctx.addMimeMapping("ext", "type");
+ *
+ * Note: If you reload the Context, all your configuration will be lost. If
+ * you need reload support, consider using a LifecycleListener to provide
+ * your configuration.
+ *
+ * TODO: add the rest
+ *
+ * @param contextPath "/" for root context.
+ * @param baseDir base dir for the context, for static files. Must exist,
+ * relative to the server home
+ */
+ public StandardContext addContext(String contextPath,
+ String baseDir) {
+ return addContext(getHost(), contextPath, baseDir);
+ }
+
+ /**
+ * Equivalent with
+ * <servlet><servlet-name><servlet-class>.
+ *
+ * In general it is better/faster to use the method that takes a
+ * Servlet as param - this one can be used if the servlet is not
+ * commonly used, and want to avoid loading all deps.
+ * ( for example: jsp servlet )
+ *
+ * You can customize the returned servlet, ex:
+ *
+ * wrapper.addInitParameter("name", "value");
+ *
+ * @param contextPath Context to add Servlet to
+ * @param servletName Servlet name (used in mappings)
+ * @param servletClass The class to be used for the Servlet
+ * @return The wrapper for the servlet
+ */
+ public StandardWrapper addServlet(String contextPath,
+ String servletName,
+ String servletClass) {
+ Container ctx = getHost().findChild(contextPath);
+ return addServlet((StandardContext) ctx,
+ servletName, servletClass);
+ }
+
+ /**
+ * Static version of {@link #addServlet(String, String, String)}
+ * @param ctx Context to add Servlet to
+ * @param servletName Servlet name (used in mappings)
+ * @param servletClass The class to be used for the Servlet
+ * @return The wrapper for the servlet
+ */
+ public static StandardWrapper addServlet(StandardContext ctx,
+ String servletName,
+ String servletClass) {
+ // will do class for name and set init params
+ StandardWrapper sw = (StandardWrapper)ctx.createWrapper();
+ sw.setServletClass(servletClass);
+ sw.setName(servletName);
+ ctx.addChild(sw);
+
+ return sw;
+ }
+
+ /**
+ * Add an existing Servlet to the context with no class.forName or
+ * initialisation.
+ * @param contextPath Context to add Servlet to
+ * @param servletName Servlet name (used in mappings)
+ * @param servlet The Servlet to add
+ * @return The wrapper for the servlet
+ */
+ public StandardWrapper addServlet(String contextPath,
+ String servletName,
+ Servlet servlet) {
+ Container ctx = getHost().findChild(contextPath);
+ return addServlet((StandardContext) ctx,
+ servletName, servlet);
+ }
+
+ /**
+ * Static version of {@link #addServlet(String, String, Servlet)}.
+ * @param ctx Context to add Servlet to
+ * @param servletName Servlet name (used in mappings)
+ * @param servlet The Servlet to add
+ * @return The wrapper for the servlet
+ */
+ public static StandardWrapper addServlet(StandardContext ctx,
+ String servletName,
+ Servlet servlet) {
+ // will do class for name and set init params
+ StandardWrapper sw = new ExistingStandardWrapper(servlet);
+ sw.setName(servletName);
+ ctx.addChild(sw);
+
+ return sw;
+ }
+
+
+ /**
+ * Initialize and start the server.
+ * @throws LifecycleException
+ */
+ public void start() throws LifecycleException {
+ getServer();
+ getConnector();
+ server.initialize();
+ server.start();
+ }
+
+ /**
+ * Stop the server.
+ * @throws LifecycleException
+ */
+ public void stop() throws LifecycleException {
+ getServer().stop();
+ }
+
+
+ /**
+ * Add a user for the in-memory realm. All created apps use this
+ * by default, can be replaced using setRealm().
+ *
+ */
+ public void addUser(String user, String pass) {
+ userPass.put(user, pass);
+ }
+
+ /**
+ * @see #addUser(String, String)
+ */
+ public void addRole(String user, String role) {
+ List<String> roles = userRoles.get(user);
+ if (roles == null) {
+ roles = new ArrayList<String>();
+ userRoles.put(user, roles);
+ }
+ roles.add(role);
+ }
+
+ // ------- Extra customization -------
+ // You can tune individual tomcat objects, using internal APIs
+
+ /**
+ * Get the default http connector. You can set more
+ * parameters - the port is already initialized.
+ *
+ * Alternatively, you can construct a Connector and set any params,
+ * then call addConnector(Connector)
+ *
+ * @return A connector object that can be customized
+ */
+ public Connector getConnector() {
+ getServer();
+ if (connector != null) {
+ return connector;
+ }
+ // This will load Apr connector if available,
+ // default to nio. I'm having strange problems with apr
+ // and for the use case the speed benefit wouldn't matter.
+
+ //connector = new Connector("HTTP/1.1");
+ try {
+ connector = new Connector("org.apache.coyote.http11.Http11Protocol");
+ connector.setPort(port);
+ service.addConnector( connector );
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ return connector;
+ }
+
+ public void setConnector(Connector connector) {
+ this.connector = connector;
+ }
+
+ /**
+ * Get the service object. Can be used to add more
+ * connectors and few other global settings.
+ */
+ public StandardService getService() {
+ getServer();
+ return service;
+ }
+
+ /**
+ * Sets the current host - all future webapps will
+ * be added to this host. When tomcat starts, the
+ * host will be the default host.
+ *
+ * @param host
+ */
+ public void setHost(StandardHost host) {
+ this.host = host;
+ }
+
+ public StandardHost getHost() {
+ if (host == null) {
+ host = new StandardHost();
+ host.setName(hostname);
+
+ getEngine().addChild( host );
+ }
+ return host;
+ }
+
+ /**
+ * Set a custom realm for auth. If not called, a simple
+ * default will be used, using an internal map.
+ *
+ * Must be called before adding a context.
+ */
+ public void setDefaultRealm(Realm realm) {
+ defaultRealm = realm;
+ }
+
+
+ /**
+ * Access to the engine, for further customization.
+ */
+ public StandardEngine getEngine() {
+ if(engine == null ) {
+ getServer();
+ engine = new StandardEngine();
+ engine.setName( "Tomcat" );
+ engine.setDefaultHost(hostname);
+ service.setContainer(engine);
+ }
+ return engine;
+ }
+
+ /**
+ * Get the server object. You can add listeners and few more
+ * customizations. JNDI is disabled by default.
+ */
+ public StandardServer getServer() {
+
+ if (server != null) {
+ return server;
+ }
+
+ initBaseDir();
+
+ System.setProperty("catalina.useNaming", "false");
+
+ server = new StandardServer();
+ server.setPort( -1 );
+
+ service = new StandardService();
+ service.setName("Tomcat");
+ server.addService( service );
+ return server;
+ }
+
+ public StandardContext addContext(StandardHost host,
+ String contextPath,
+ String dir) {
+ silence(contextPath);
+ StandardContext ctx = new StandardContext();
+ ctx.setPath( contextPath );
+ ctx.setDocBase(dir);
+ ctx.addLifecycleListener(new FixContextListener());
+
+ if (host == null) {
+ getHost().addChild(ctx);
+ } else {
+ host.addChild(ctx);
+ }
+ return ctx;
+ }
+
+ public StandardContext addWebapp(StandardHost host,
+ String url, String path) {
+ silence(url);
+
+ StandardContext ctx = new StandardContext();
+ ctx.setPath( url );
+ ctx.setDocBase(path);
+ if (defaultRealm == null) {
+ initSimpleAuth();
+ }
+ ctx.setRealm(defaultRealm);
+ ctx.addLifecycleListener(new DefaultWebXmlListener());
+
+ ContextConfig ctxCfg = new ContextConfig();
+ ctx.addLifecycleListener( ctxCfg );
+ // prevent it from looking ( if it finds one - it'll have dup error )
+ ctxCfg.setDefaultWebXml("org/apache/catalin/startup/NO_DEFAULT_XML");
+
+ if (host == null) {
+ getHost().addChild(ctx);
+ } else {
+ host.addChild(ctx);
+ }
+
+ return ctx;
+ }
+
+
+
+
+
+ // ---------- Helper methods and classes -------------------
+
+ /**
+ * Initialize an in-memory realm. You can replace it
+ * for contexts with a real one.
+ */
+ protected void initSimpleAuth() {
+ defaultRealm = new RealmBase() {
+ @Override
+ protected String getName() {
+ return "Simple";
+ }
+
+ @Override
+ protected String getPassword(String username) {
+ return userPass.get(username);
+ }
+
+ @Override
+ protected Principal getPrincipal(final String username) {
+ Principal p = userPrincipals.get(username);
+ if (p == null) {
+ String pass = userPass.get(username);
+ if (pass != null) {
+ p = new Principal() {
+
+ @Override
+ public String getName() {
+ return username;
+ }
+
+ };
+ }
+ }
+ return p;
+ }
+
+ };
+ }
+
+ protected void initBaseDir() {
+ if (basedir == null) {
+ basedir = System.getProperty("catalina.base");
+ }
+ if (basedir == null) {
+ basedir = System.getProperty("catalina.home");
+ }
+ if (basedir == null) {
+ // Create a temp dir.
+ basedir = System.getProperty("user.dir") +
+ "/tomcat." + port;
+ File home = new File(basedir);
+ home.mkdir();
+ if (!home.isAbsolute()) {
+ try {
+ basedir = home.getCanonicalPath();
+ } catch (IOException e) {
+ basedir = home.getAbsolutePath();
+ }
+ }
+ }
+ System.setProperty("catalina.home", basedir);
+ System.setProperty("catalina.base", basedir);
+ }
+
+ static String[] silences = new String[] {
+ "org.apache.coyote.http11.Http11Protocol",
+ "org.apache.catalina.core.StandardService",
+ "org.apache.catalina.core.StandardEngine",
+ "org.apache.catalina.startup.ContextConfig",
+ "org.apache.catalina.core.ApplicationContext",
+ "org.apache.catalina.core.AprLifecycleListener"
+ };
+
+ /**
+ * Controls if the loggers will be silenced or not.
+ * @param silent <code>true</code> sets the log level to WARN for the
+ * loggers that log information on Tomcat start up. This
+ * prevents the usual startup information being logged.
+ * <code>false</code> sets the log level to the default
+ * level of INFO.
+ */
+ public void setSilent(boolean silent) {
+ for (String s : silences) {
+ if (silent) {
+ Logger.getLogger(s).setLevel(Level.WARNING);
+ } else {
+ Logger.getLogger(s).setLevel(Level.INFO);
+ }
+ }
+ }
+
+ private void silence(String ctx) {
+ String base = "org.apache.catalina.core.ContainerBase.[default].[";
+ base += getHost().getName();
+ base += "].[";
+ base += ctx;
+ base += "]";
+ Logger.getLogger(base).setLevel(Level.WARNING);
+ }
+
+ /**
+ * Enables JNDI naming which is disabled by default.
+ */
+ public void enableNaming() {
+ // Make sure getServer() has been called as that is where naming is
+ // disabled
+ getServer();
+
+ System.setProperty("catalina.useNaming", "true");
+ String value = "org.apache.naming";
+ String oldValue =
+ System.getProperty(javax.naming.Context.URL_PKG_PREFIXES);
+ if (oldValue != null) {
+ value = value + ":" + oldValue;
+ }
+ System.setProperty(javax.naming.Context.URL_PKG_PREFIXES, value);
+ value = System.getProperty
+ (javax.naming.Context.INITIAL_CONTEXT_FACTORY);
+ if (value == null) {
+ System.setProperty
+ (javax.naming.Context.INITIAL_CONTEXT_FACTORY,
+ "org.apache.naming.java.javaURLContextFactory");
+ }
+ }
+
+ /**
+ * Provide default configuration for a context. This is the programmatic
+ * equivalent of the default web.xml.
+ *
+ * TODO: in normal tomcat, if default-web.xml is not found, use this
+ * method
+ *
+ * @param contextPath The context to set the defaults for
+ */
+ public void initWebappDefaults(String contextPath) {
+ Container ctx = getHost().findChild(contextPath);
+ initWebappDefaults((StandardContext) ctx);
+ }
+
+ /**
+ * Static version of {@link #initWebappDefaults(String)}
+ * @param ctx The context to set the defaults for
+ */
+ public static void initWebappDefaults(StandardContext ctx) {
+ // Default servlet
+ StandardWrapper servlet = addServlet(
+ ctx, "default", "org.apache.catalina.servlets.DefaultServlet");
+ servlet.setLoadOnStartup(1);
+
+ // JSP servlet (by class name - to avoid loading all deps)
+ servlet = addServlet(
+ ctx, "jsp", "org.apache.jasper.servlet.JspServlet");
+ servlet.addInitParameter("fork", "false");
+ servlet.setLoadOnStartup(3);
+
+ // Servlet mappings
+ ctx.addServletMapping("/", "default");
+ ctx.addServletMapping("*.jsp", "jsp");
+ ctx.addServletMapping("*.jspx", "jsp");
+
+ // Sessions
+ ctx.setManager( new StandardManager());
+ ctx.setSessionTimeout(30);
+
+ // MIME mappings
+ for (int i = 0; i < DEFAULT_MIME_MAPPINGS.length; ) {
+ ctx.addMimeMapping(DEFAULT_MIME_MAPPINGS[i++],
+ DEFAULT_MIME_MAPPINGS[i++]);
+ }
+
+ // Welcome files
+ ctx.addWelcomeFile("index.html");
+ ctx.addWelcomeFile("index.htm");
+ ctx.addWelcomeFile("index.jsp");
+ }
+
+
+ /**
+ * Fix startup sequence - required if you don't use web.xml.
+ *
+ * The start() method in context will set 'configured' to false - and
+ * expects a listener to set it back to true.
+ */
+ public static class FixContextListener implements LifecycleListener {
+
+ public void lifecycleEvent(LifecycleEvent event) {
+ try {
+ Context context = (Context) event.getLifecycle();
+ if (event.getType().equals(Lifecycle.START_EVENT)) {
+ context.setConfigured(true);
+ }
+ } catch (ClassCastException e) {
+ return;
+ }
+ }
+
+ }
+
+
+ /**
+ * Fix reload - required if reloading and using programmatic configuration.
+ * When a context is reloaded, any programmatic configuration is lost. This
+ * listener sets the equivalent of conf/web.xml when the context starts. The
+ * context needs to be an instance of StandardContext for this listener to
+ * have any effect.
+ */
+ public static class DefaultWebXmlListener implements LifecycleListener {
+ public void lifecycleEvent(LifecycleEvent event) {
+ if (Lifecycle.BEFORE_START_EVENT.equals(event.getType()) &&
+ event.getLifecycle() instanceof StandardContext) {
+ initWebappDefaults((StandardContext) event.getLifecycle());
+ }
+ }
+ }
+
+
+ /**
+ * Helper class for wrapping existing servlets. This disables servlet
+ * lifecycle and normal reloading, but also reduces overhead and provide
+ * more direct control over the servlet.
+ */
+ public static class ExistingStandardWrapper extends StandardWrapper {
+ private Servlet existing;
+ boolean init = false;
+
+ public ExistingStandardWrapper( Servlet existing ) {
+ this.existing = existing;
+ }
+ @Override
+ public synchronized Servlet loadServlet() throws ServletException {
+ if (!init) {
+ existing.init(facade);
+ init = true;
+ }
+ return existing;
+
+ }
+ @Override
+ public long getAvailable() {
+ return 0;
+ }
+ @Override
+ public boolean isUnavailable() {
+ return false;
+ }
+ }
+
+ /**
+ * TODO: would a properties resource be better ? Or just parsing
+ * /etc/mime.types ?
+ * This is needed because we don't use the default web.xml, where this
+ * is encoded.
+ */
+ public static final String[] DEFAULT_MIME_MAPPINGS = {
+ "abs", "audio/x-mpeg",
+ "ai", "application/postscript",
+ "aif", "audio/x-aiff",
+ "aifc", "audio/x-aiff",
+ "aiff", "audio/x-aiff",
+ "aim", "application/x-aim",
+ "art", "image/x-jg",
+ "asf", "video/x-ms-asf",
+ "asx", "video/x-ms-asf",
+ "au", "audio/basic",
+ "avi", "video/x-msvideo",
+ "avx", "video/x-rad-screenplay",
+ "bcpio", "application/x-bcpio",
+ "bin", "application/octet-stream",
+ "bmp", "image/bmp",
+ "body", "text/html",
+ "cdf", "application/x-cdf",
+ "cer", "application/x-x509-ca-cert",
+ "class", "application/java",
+ "cpio", "application/x-cpio",
+ "csh", "application/x-csh",
+ "css", "text/css",
+ "dib", "image/bmp",
+ "doc", "application/msword",
+ "dtd", "application/xml-dtd",
+ "dv", "video/x-dv",
+ "dvi", "application/x-dvi",
+ "eps", "application/postscript",
+ "etx", "text/x-setext",
+ "exe", "application/octet-stream",
+ "gif", "image/gif",
+ "gtar", "application/x-gtar",
+ "gz", "application/x-gzip",
+ "hdf", "application/x-hdf",
+ "hqx", "application/mac-binhex40",
+ "htc", "text/x-component",
+ "htm", "text/html",
+ "html", "text/html",
+ "hqx", "application/mac-binhex40",
+ "ief", "image/ief",
+ "jad", "text/vnd.sun.j2me.app-descriptor",
+ "jar", "application/java-archive",
+ "java", "text/plain",
+ "jnlp", "application/x-java-jnlp-file",
+ "jpe", "image/jpeg",
+ "jpeg", "image/jpeg",
+ "jpg", "image/jpeg",
+ "js", "text/javascript",
+ "jsf", "text/plain",
+ "jspf", "text/plain",
+ "kar", "audio/x-midi",
+ "latex", "application/x-latex",
+ "m3u", "audio/x-mpegurl",
+ "mac", "image/x-macpaint",
+ "man", "application/x-troff-man",
+ "mathml", "application/mathml+xml",
+ "me", "application/x-troff-me",
+ "mid", "audio/x-midi",
+ "midi", "audio/x-midi",
+ "mif", "application/x-mif",
+ "mov", "video/quicktime",
+ "movie", "video/x-sgi-movie",
+ "mp1", "audio/x-mpeg",
+ "mp2", "audio/x-mpeg",
+ "mp3", "audio/x-mpeg",
+ "mp4", "video/mp4",
+ "mpa", "audio/x-mpeg",
+ "mpe", "video/mpeg",
+ "mpeg", "video/mpeg",
+ "mpega", "audio/x-mpeg",
+ "mpg", "video/mpeg",
+ "mpv2", "video/mpeg2",
+ "ms", "application/x-wais-source",
+ "nc", "application/x-netcdf",
+ "oda", "application/oda",
+ "odb", "application/vnd.oasis.opendocument.database",
+ "odc", "application/vnd.oasis.opendocument.chart",
+ "odf", "application/vnd.oasis.opendocument.formula",
+ "odg", "application/vnd.oasis.opendocument.graphics",
+ "odi", "application/vnd.oasis.opendocument.image",
+ "odm", "application/vnd.oasis.opendocument.text-master",
+ "odp", "application/vnd.oasis.opendocument.presentation",
+ "ods", "application/vnd.oasis.opendocument.spreadsheet",
+ "odt", "application/vnd.oasis.opendocument.text",
+ "otg", "application/vnd.oasis.opendocument.graphics-template",
+ "oth", "application/vnd.oasis.opendocument.text-web",
+ "otp", "application/vnd.oasis.opendocument.presentation-template",
+ "ots", "application/vnd.oasis.opendocument.spreadsheet-template ",
+ "ott", "application/vnd.oasis.opendocument.text-template",
+ "ogx", "application/ogg",
+ "ogv", "video/ogg",
+ "oga", "audio/ogg",
+ "ogg", "audio/ogg",
+ "spx", "audio/ogg",
+ "faca", "audio/flac",
+ "anx", "application/annodex",
+ "axa", "audio/annodex",
+ "axv", "video/annodex",
+ "xspf", "application/xspf+xml",
+ "pbm", "image/x-portable-bitmap",
+ "pct", "image/pict",
+ "pdf", "application/pdf",
+ "pgm", "image/x-portable-graymap",
+ "pic", "image/pict",
+ "pict", "image/pict",
+ "pls", "audio/x-scpls",
+ "png", "image/png",
+ "pnm", "image/x-portable-anymap",
+ "pnt", "image/x-macpaint",
+ "ppm", "image/x-portable-pixmap",
+ "ppt", "application/vnd.ms-powerpoint",
+ "pps", "application/vnd.ms-powerpoint",
+ "ps", "application/postscript",
+ "psd", "image/x-photoshop",
+ "qt", "video/quicktime",
+ "qti", "image/x-quicktime",
+ "qtif", "image/x-quicktime",
+ "ras", "image/x-cmu-raster",
+ "rdf", "application/rdf+xml",
+ "rgb", "image/x-rgb",
+ "rm", "application/vnd.rn-realmedia",
+ "roff", "application/x-troff",
+ "rtf", "application/rtf",
+ "rtx", "text/richtext",
+ "sh", "application/x-sh",
+ "shar", "application/x-shar",
+ /*"shtml", "text/x-server-parsed-html",*/
+ "smf", "audio/x-midi",
+ "sit", "application/x-stuffit",
+ "snd", "audio/basic",
+ "src", "application/x-wais-source",
+ "sv4cpio", "application/x-sv4cpio",
+ "sv4crc", "application/x-sv4crc",
+ "svg", "image/svg+xml",
+ "svgz", "image/svg+xml",
+ "swf", "application/x-shockwave-flash",
+ "t", "application/x-troff",
+ "tar", "application/x-tar",
+ "tcl", "application/x-tcl",
+ "tex", "application/x-tex",
+ "texi", "application/x-texinfo",
+ "texinfo", "application/x-texinfo",
+ "tif", "image/tiff",
+ "tiff", "image/tiff",
+ "tr", "application/x-troff",
+ "tsv", "text/tab-separated-values",
+ "txt", "text/plain",
+ "ulw", "audio/basic",
+ "ustar", "application/x-ustar",
+ "vxml", "application/voicexml+xml",
+ "xbm", "image/x-xbitmap",
+ "xht", "application/xhtml+xml",
+ "xhtml", "application/xhtml+xml",
+ "xls", "application/vnd.ms-excel",
+ "xml", "application/xml",
+ "xpm", "image/x-xpixmap",
+ "xsl", "application/xml",
+ "xslt", "application/xslt+xml",
+ "xul", "application/vnd.mozilla.xul+xml",
+ "xwd", "image/x-xwindowdump",
+ "vsd", "application/x-visio",
+ "wav", "audio/x-wav",
+ "wbmp", "image/vnd.wap.wbmp",
+ "wml", "text/vnd.wap.wml",
+ "wmlc", "application/vnd.wap.wmlc",
+ "wmls", "text/vnd.wap.wmlscript",
+ "wmlscriptc", "application/vnd.wap.wmlscriptc",
+ "wmv", "video/x-ms-wmv",
+ "wrl", "x-world/x-vrml",
+ "wspolicy", "application/wspolicy+xml",
+ "Z", "application/x-compress",
+ "z", "application/x-compress",
+ "zip", "application/zip"
+ };
+}
import org.apache.catalina.LifecycleException;
import org.apache.catalina.connector.Connector;
-import org.apache.catalina.startup.Tomcat;
-import org.apache.coyote.lite.LiteProtocolHandler;
import org.apache.tomcat.lite.http.DefaultHttpConnector;
import org.apache.tomcat.lite.http.HttpChannel;
import org.apache.tomcat.lite.http.HttpConnector;
if (tomcat == null) {
try {
tomcat = new Tomcat();
+
tomcat.setPort(8885);
- tomcat.setBaseDir("output/build");
+ tomcat.setBaseDir("../../output/build/webapps");
tomcat.addWebapp("/examples", "examples");
tomcat.addWebapp("/", "ROOT");
//tomcat.addServlet(ctx, "name", "class");
// ctx.addServletMapping("/foo/*", "name");
- litePH = setUp(tomcat);
+ litePH = setUp(tomcat, 8885);
tomcat.start();
} catch (LifecycleException e) {
}
}
- public static LiteProtocolHandler setUp(Tomcat tomcat) {
- Connector connector = new Connector(LiteProtocolHandler.class.getName());
- tomcat.getService().addConnector(connector);
- connector.setPort(8885);
- tomcat.setConnector(connector);
- LiteProtocolHandler ph =
- (LiteProtocolHandler) connector.getProtocolHandler();
- return ph;
+ public static LiteProtocolHandler setUp(Tomcat tomcat, int port) {
+ Connector connector;
+ try {
+ connector = new Connector(LiteProtocolHandler.class.getName());
+ tomcat.getService().addConnector(connector);
+ connector.setPort(port);
+ tomcat.setConnector(connector);
+ LiteProtocolHandler ph =
+ (LiteProtocolHandler) connector.getProtocolHandler();
+ return ph;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
}
package org.apache.tomcat.lite.load;
+import java.io.File;
import java.io.IOException;
+import java.lang.management.ManagementFactory;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.management.InstanceNotFoundException;
+import javax.management.MBeanException;
+import javax.management.MBeanServer;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+import javax.management.ReflectionException;
import junit.framework.TestCase;
import org.apache.tomcat.lite.TestMain;
-import org.apache.tomcat.lite.http.DefaultHttpConnector;
import org.apache.tomcat.lite.http.HttpChannel;
import org.apache.tomcat.lite.http.HttpConnector;
import org.apache.tomcat.lite.http.HttpRequest;
-import org.apache.tomcat.lite.http.HttpChannel.HttpService;
import org.apache.tomcat.lite.http.HttpChannel.RequestCompleted;
+import org.apache.tomcat.lite.io.BBuffer;
+import org.apache.tomcat.lite.io.IOBuffer;
+
+/*
+ Notes on memory use ( from heap dumps ):
+ - buffers are not yet recycled ( the BBuffers used in channels )
+
+ - each active connection consumes at least 26k - 2 buffers + head buffer
+ ( 8k each )
+ TODO: could 'peak' in the In buffer and move headRecv to HttpChannel
+
+
+ - HttpChannel keeps about 64K ( for the hello world ).
+ -- res is 25k
+ -- req is 32k, BufferedIOReader 16k,
+
+ TODO:
+ - leak in NioThread.active - closed sockets not removed
+ - need to rate-limit and queue requests - OOM
+ - timeouts
+ - seems few responses missing on large async requests (URL works)
+ */
+/**
+ * Long running test - async tests are failing since rate control
+ * is not implemented ( too many outstanding requests - OOM ),
+ * it seems there is a bug as well.
+ */
public class LiveHttpThreadedTest extends TestCase {
- HttpConnector staticMain = TestMain.initTestEnv();
+ HttpConnector clientCon = TestMain.getClientAndInit();
+ ThreadRunner tr;
+ static MBeanServer server;
+ AtomicInteger ok = new AtomicInteger();
+ Object lock = new Object();
+ int reqCnt;
+
+ Map<HttpRequest, HttpRequest> active = new HashMap();
- int tCount = 1;
- Thread[] threads = new Thread[tCount];
- int[] ok = new int[tCount];
- private int rCount = 100;
+ public void xtest1000Async() throws Exception {
+ try {
+ asyncRequest(10, 100);
+ } finally {
+ dumpHeap("heapAsync.bin");
+ }
+
+ }
+
+ public void xtest10000Async() throws Exception {
+ try {
+ asyncRequest(20, 500);
+ } finally {
+ dumpHeap("heapAsync.bin");
+ }
+ }
- public void xtestSimpleRequest() throws Exception {
- long t0 = System.currentTimeMillis();
- for (int i = 0; i < tCount; i++) {
- final int j = i;
- threads[i] = new Thread(new Runnable() {
- public void run() {
- makeRequests(j, true);
- }
- });
- threads[i].start();
- }
-
- int res = 0;
- for (int i = 0; i < tCount; i++) {
- threads[i].join();
- res += ok[i];
- }
- long t1 = System.currentTimeMillis();
- System.err.println("Time: " + (t1 - t0) + " " + res);
+ public void asyncRequest(int thr, int perthr) throws Exception {
+ reqCnt = thr * perthr;
+ long t0 = System.currentTimeMillis();
+ tr = new ThreadRunner(thr, perthr) {
+ public void makeRequest(int i) throws Exception {
+ HttpRequest cstate = clientCon.request("localhost", 8802);
+ synchronized (active) {
+ active.put(cstate, cstate);
+ }
+
+ cstate.requestURI().set("/hello");
+ cstate.setCompletedCallback(reqCallback);
+
+ // Send the request, wait response
+ Thread.currentThread().sleep(20);
+ cstate.send();
+ }
+ };
+ tr.run();
+ assertEquals(0, tr.errors.get());
+ synchronized (lock) {
+ lock.wait(reqCnt * 100);
+ }
+ assertEquals(reqCnt, ok.get());
+ System.err.println(reqCnt + " Async requests: " + (System.currentTimeMillis() - t0));
+ }
+
+ public void xtestURLRequest() throws Exception {
+ urlRequest(10, 200);
}
- public void testSimpleRequestNB() throws Exception {
- long t0 = System.currentTimeMillis();
- for (int i = 0; i < tCount; i++) {
- final int j = i;
- threads[i] = new Thread(new Runnable() {
- public void run() {
- makeRequests(j, false);
- }
- });
- threads[i].start();
- }
-
- int res = 0;
- for (int i = 0; i < tCount; i++) {
- threads[i].join();
- res += ok[i];
- }
- long t1 = System.currentTimeMillis();
- System.err.println("TimeNB: " + (t1 - t0) + " " + res);
+ public void testURLRequest2() throws Exception {
+ urlRequest(40, 500);
}
- void makeRequests(int t, boolean b) {
- for (int i = 0; i < rCount ; i++) {
+ public void urlRequest(int thr, int cnt) throws Exception {
+
+
try {
- //System.err.println("MakeReq " + t + " " + i);
- makeRequest(t, b);
- } catch (Exception e) {
- e.printStackTrace();
+ HttpConnector testServer = TestMain.testServer;
+
+ tr = new ThreadRunner(thr, cnt) {
+
+ public void makeRequest(int i) throws Exception {
+ try {
+ URL url = new URL("http://localhost:8802/hello");
+ HttpURLConnection con = (HttpURLConnection) url.openConnection();
+ con.setReadTimeout(4000000);
+ con.connect();
+ if (con.getResponseCode() != 200) {
+ errors.incrementAndGet();
+ }
+ BBuffer out = new IOBuffer().append(con.getInputStream()).copyAll(null);
+ if (!"Hello world".equals(out.toString())) {
+ errors.incrementAndGet();
+ System.err.println("bad result " + out);
+ }
+ } catch(Throwable t) {
+ t.printStackTrace();
+ errors.incrementAndGet();
+ }
+ }
+ };
+ tr.run();
+ assertEquals(0, tr.errors.get());
+
+ //assertEquals(testServer., actual)
+ } finally {
+ dumpHeap("heapURLReq.bin");
}
- }
}
+
+ // TODO: move to a servlet
+ private void dumpHeap(String file) throws InstanceNotFoundException,
+ MBeanException, ReflectionException, MalformedObjectNameException {
+
+ if (server == null) {
+ server = ManagementFactory.getPlatformMBeanServer();
+
+ }
+ File f1 = new java.io.File(file);
+ if (f1.exists()) {
+ f1.delete();
+ }
+ server.invoke(new ObjectName("com.sun.management:type=HotSpotDiagnostic"),
+ "dumpHeap",
+ new Object[] {file, Boolean.FALSE /* live */},
+ new String[] {String.class.getName(), "boolean"});
+ }
+
- static RequestCompleted reqCallback = new RequestCompleted() {
+ RequestCompleted reqCallback = new RequestCompleted() {
@Override
public void handle(HttpChannel data, Object extraData)
throws IOException {
- //dumpHead(cstate);
- //System.err.println("DATA\n" + cstate.output.toString() + "\n----");
- //assertTrue(cstate.bodyRecvBuffer.toString().indexOf("AAA") >= 0);
-
+ String out = data.getIn().copyAll(null).toString();
+ if (200 != data.getResponse().getStatus()) {
+ System.err.println("Wrong status");
+ tr.errors.incrementAndGet();
+ }
+ if (!"Hello world".equals(out)) {
+ tr.errors.incrementAndGet();
+ System.err.println("bad result " + out);
+ }
+ synchronized (active) {
+ active.remove(data.getRequest());
+ }
data.release();
+ int okres = ok.incrementAndGet();
+ if (okres >= reqCnt) {
+ synchronized (lock) {
+ lock.notify();
+ }
+ }
}
-
};
- void makeRequest(int i, boolean block) throws Exception {
- HttpRequest cstate = DefaultHttpConnector.get().request("localhost", 8802);
-
- cstate.requestURI().set("/hello");
- cstate.setCompletedCallback(reqCallback);
-
- // Send the request, wait response
- cstate.send();
- }
}
--- /dev/null
+/*
+ */
+package org.apache.tomcat.lite.load;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class ThreadRunner {
+ int tCount = 10;
+ int rCount = 100;
+ Thread[] threads;
+ int[] ok;
+
+ int sleepTime = 0;
+
+ long time;
+ protected AtomicInteger errors = new AtomicInteger();
+
+ public ThreadRunner(int threads, int count) {
+ tCount = threads;
+ rCount = count;
+ this.threads = new Thread[tCount];
+ ok = new int[tCount];
+ }
+
+ public void run() {
+ long t0 = System.currentTimeMillis();
+ for (int i = 0; i < tCount; i++) {
+ final int j = i;
+ threads[i] = new Thread(new Runnable() {
+ public void run() {
+ makeRequests(j);
+ }
+ });
+ threads[i].start();
+ }
+
+ int res = 0;
+ for (int i = 0; i < tCount; i++) {
+ try {
+ threads[i].join();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ res += ok[i];
+ }
+ long t1 = System.currentTimeMillis();
+ time = t1 - t0;
+ System.err.println("TimeNB: " + (t1 - t0) + " " + res);
+ }
+
+ public void makeRequests(int cnt) {
+ for (int i = 0; i < rCount ; i++) {
+ try {
+ //System.err.println("MakeReq " + t + " " + i);
+ makeRequest(cnt);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ public void makeRequest(int i) throws Exception {
+
+ }
+}
\ No newline at end of file
public abstract class TomcatLiteWatchdog extends WatchdogClient {
public TomcatLiteWatchdog() {
+ super();
goldenDir = getWatchdogdir() + "/src/clients/org/apache/jcheck/servlet/client/";
testMatch =
//"HttpServletResponseWrapperSetStatusMsgTest";
targetMatch = "gtestservlet-test";
}
+ public TomcatLiteWatchdog(String s) {
+ this();
+ super.single = s;
+ }
+
protected void beforeSuite() {
// required for the tests
System.setProperty("org.apache.coyote.USE_CUSTOM_STATUS_MSG_IN_HEADER",
protected abstract void addConnector(TomcatLite liteServer);
- TomcatLite tomcatForWatchdog;
-
public void initServerWithWatchdog(String wdDir) throws ServletException,
IOException {
+ TomcatLite tomcatForWatchdog;
+
File f = new File(wdDir + "/build/webapps");
tomcatForWatchdog = new TomcatLite();
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
-public class WatchdogClient {
+public class WatchdogClient implements Test {
protected String goldenDir;
protected String testMatch;
};
protected String targetMatch;
-
-
+
+ protected int port;
+
Properties props = new Properties();
protected void beforeSuite() {
}
public Test getSuite() {
- return getSuite(8080);
+ return getSuite(port);
}
/**
for (int j = 0; j < watchDogL.getLength(); j++) {
Element watchE = (Element) watchDogL.item(j);
String testName = watchE.getAttribute("testName");
+ if (single != null && !testName.equals(single)) {
+ continue;
+ }
if (testMatch != null) {
if (!testName.startsWith(testMatch)) {
continue;
continue;
}
}
- testName = testName + ";" + this.getClass().getName();
+ testName = testName + "(" + this.getClass().getName() + ")";
WatchdogTestCase test = new WatchdogTestCase(watchE, props, testName);
tests.addTest(test);
+ if (single != null) {
+ singleTest = test;
+ break;
+ }
}
}
}
}
+ // Support for running a single test in the suite
+
+ protected String single;
+ WatchdogTestCase singleTest;
+
+ @Override
+ public int countTestCases() {
+ return 1;
+ }
+
+ @Override
+ public void run(TestResult result) {
+ getSuite();
+ if (singleTest != null) {
+ beforeSuite();
+ singleTest.run(result);
+ afterSuite(result);
+ }
+ }
+
}
System.out.println( " Socket Exception: " + ex );
return;
}
- //socket.setSoTimeout(2000);
+ socket.setSoTimeout(10000);
//socket obtained, rebuild the request.
rebuildRequest(client, client.request, socket);
import junit.framework.AssertionFailedError;
import junit.framework.Test;
import junit.framework.TestResult;
-import junit.framework.TestSuite;
import org.apache.tomcat.integration.DynamicObject;
import org.apache.tomcat.integration.simple.AntProperties;
private Properties props;
- private WatchdogTestCase delegate;
private WatchdogClient wc;
- public WatchdogTestCase(String s) throws Throwable {
- String[] comp = s.split(";");
- if (comp.length < 2) {
- return;
- }
- Class c = Class.forName(comp[1]);
- wc = (WatchdogClient) c.newInstance();
- TestSuite suite = (TestSuite) wc.getSuite();
- // need to encode the base, file, etc in the test name
-
- System.err.println(s);
-
- for (int i = 0; i < suite.testCount(); i++) {
- WatchdogTestCase t = (WatchdogTestCase) suite.testAt(i);
- if (s.equals(t.getName())) {
- delegate = t;
- return;
- }
- }
+ public WatchdogTestCase() {
+
}
-
+
public WatchdogTestCase(Element watchE, Properties props, String testName) {
this.testName = testName;
this.watchE = watchE;
}
public String getName() {
- return testName;
+ return testName == null ? "WatchdogTest" : testName;
}
+ public String toString() {
+ return getName();
+ }
+
public void testDummy() {
}
public void run(TestResult res) {
- if (delegate != null) {
- // Single method run
- wc.beforeSuite();
- delegate.run(res);
- wc.afterSuite(res);
- return;
- }
if (watchE == null) {
res.endTest(this);
return;