dims 2002/06/07 11:31:58
Modified: java/test/wsdl/multithread MultithreadTestCase.java
java/src/org/apache/axis/transport/http
SimpleAxisServer.java
Added: java/src/org/apache/axis/transport/http
SimpleAxisWorker.java
Log:
Objectives:
Improve performance of SimpleAxisServer for more stringent testing in a
multi-threaded environment.
Steps:
- Start a new thread in SimpleAxisServer for each client socket.
- Extract code from SimpleAxisServer into SimpleAxisWorker
Note:
The success ratio of MultithreadTestCase jumped from ~205/400 to ~320/400 on both
JDK13 and JDK14
Revision Changes Path
1.8 +10 -11 xml-axis/java/test/wsdl/multithread/MultithreadTestCase.java
Index: MultithreadTestCase.java
===================================================================
RCS file: /home/cvs/xml-axis/java/test/wsdl/multithread/MultithreadTestCase.java,v
retrieving revision 1.7
retrieving revision 1.8
diff -u -r1.7 -r1.8
--- MultithreadTestCase.java 1 Apr 2002 20:12:17 -0000 1.7
+++ MultithreadTestCase.java 7 Jun 2002 18:31:58 -0000 1.8
@@ -1,26 +1,20 @@
package test.wsdl.multithread;
-import java.net.ConnectException;
-
-import java.rmi.RemoteException;
-
-import javax.xml.rpc.ServiceException;
-
import junit.framework.AssertionFailedError;
import junit.framework.TestCase;
-
import org.apache.axis.AxisFault;
-
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-
+import samples.addr.Address;
import samples.addr.AddressBook;
-import samples.addr.AddressBookServiceLocator;
import samples.addr.AddressBookSOAPBindingStub;
-import samples.addr.Address;
+import samples.addr.AddressBookServiceLocator;
import samples.addr.Phone;
import samples.addr.StateType;
+import javax.xml.rpc.ServiceException;
+import java.net.ConnectException;
+
/**
* This test calls the stub multiple times from multiple threads. Before the
* stub was made threadsafe, there was a good chance this test would fail with an
@@ -141,5 +135,10 @@
throw error;
}
} // testMultithreading
+
+ public static void main(String[] args) {
+ MultithreadTestCase testCase = new
MultithreadTestCase("MultithreadTestCase");
+ testCase.testMultithreading();
+ }
} // class MultithreadTestCase
1.61 +43 -655
xml-axis/java/src/org/apache/axis/transport/http/SimpleAxisServer.java
Index: SimpleAxisServer.java
===================================================================
RCS file:
/home/cvs/xml-axis/java/src/org/apache/axis/transport/http/SimpleAxisServer.java,v
retrieving revision 1.60
retrieving revision 1.61
diff -u -r1.60 -r1.61
--- SimpleAxisServer.java 4 Jun 2002 01:19:46 -0000 1.60
+++ SimpleAxisServer.java 7 Jun 2002 18:31:58 -0000 1.61
@@ -53,31 +53,16 @@
* <http://www.apache.org/>.
*/
-package org.apache.axis.transport.http ;
+package org.apache.axis.transport.http;
-import org.apache.axis.AxisFault;
-import org.apache.axis.Constants;
-import org.apache.axis.Message;
-import org.apache.axis.MessageContext;
-import org.apache.axis.encoding.Base64;
-import org.apache.axis.message.SOAPEnvelope;
-import org.apache.axis.message.SOAPFaultElement;
import org.apache.axis.server.AxisServer;
import org.apache.axis.session.Session;
import org.apache.axis.session.SimpleSession;
-import org.apache.axis.utils.Options;
import org.apache.axis.utils.JavaUtils;
-import org.apache.axis.utils.XMLUtils;
-
+import org.apache.axis.utils.Options;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-import org.w3c.dom.Document;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InterruptedIOException;
-import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.ServerSocket;
import java.net.Socket;
@@ -94,8 +79,7 @@
*/
public class SimpleAxisServer implements Runnable {
protected static Log log =
- LogFactory.getLog(SimpleAxisServer.class.getName());
-
+ LogFactory.getLog(SimpleAxisServer.class.getName());
// session state.
// This table maps session keys (random numbers) to SimpleAxisSession objects.
@@ -110,7 +94,26 @@
// Are we doing sessions?
// Set this to false if you don't want any session overhead.
- public static boolean doSessions = true;
+ private static boolean doSessions = true;
+
+ protected boolean isSessionUsed() {
+ return doSessions;
+ }
+
+ protected Session createSession(String cooky) {
+ // is there a session already?
+ Session session = null;
+ if (sessions.containsKey(cooky)) {
+ session = (Session) sessions.get(cooky);
+ } else {
+ // no session for this cooky, bummer
+ session = new SimpleSession();
+
+ // ADD CLEANUP LOGIC HERE if needed
+ sessions.put(cooky, session);
+ }
+ return session;
+ }
// What is our current session index?
// This is a monotonically increasing, non-thread-safe integer
@@ -119,663 +122,44 @@
// Axis server (shared between instances)
private static AxisServer myAxisServer = null;
- private static synchronized AxisServer getAxisServer() {
+
+ protected static synchronized AxisServer getAxisServer() {
if (myAxisServer == null) {
myAxisServer = new AxisServer();
}
return myAxisServer;
}
- // HTTP prefix
- private static byte HTTP[] = "HTTP/1.0 ".getBytes();
-
- // HTTP status codes
- private static byte OK[] = ("200 " +
JavaUtils.getMessage("ok00")).getBytes();
- private static byte UNAUTH[] = ("401 " +
JavaUtils.getMessage("unauth00")).getBytes();
- private static byte ISE[] = ("500 " +
JavaUtils.getMessage("internalError01")).getBytes();
-
- // Standard MIME headers for XML payload
- private static byte XML_MIME_STUFF[] =
- ( "\r\nContent-Type: text/xml; charset=utf-8\r\n" +
- "Content-Length: ").getBytes();
-
- // Standard MIME headers for HTML payload
- private static byte HTML_MIME_STUFF[] =
- ( "\r\nContent-Type: text/html; charset=utf-8\r\n" +
- "Content-Length: ").getBytes();
-
- // Mime/Content separator
- private static byte SEPARATOR[] = "\r\n\r\n".getBytes();
-
- // Tiddly little response
- private static final String responseStr =
- "<html><head><title>SimpleAxisServer</title></head>" +
- "<body><h1>SimpleAxisServer</h1>" +
- JavaUtils.getMessage("reachedServer00") +
- "</html>";
- private static byte cannedHTMLResponse[] = responseStr.getBytes();
-
- // Axis specific constants
- private static String transportName = "SimpleHTTP";
-
// are we stopped?
// latch to true if stop() is called
private boolean stopped = false;
/**
- * The main workhorse method.
- *
* Accept requests from a given TCP port and send them through the
* Axis engine for processing.
*/
public void run() {
- log.info(JavaUtils.getMessage("start00", "SimpleAxisServer",
- new Integer(getServerSocket().getLocalPort()).toString()));
-
- // create an Axis server
- AxisServer engine = getAxisServer();
- //engine.init();
-
- // create and initialize a message context
- MessageContext msgContext = new MessageContext(engine);
- Message requestMsg;
-
- // Reusuable, buffered, content length controlled, InputStream
- NonBlockingBufferedInputStream is =
- new NonBlockingBufferedInputStream();
-
- // buffers for the headers we care about
- StringBuffer soapAction = new StringBuffer();
- StringBuffer httpRequest = new StringBuffer();
- StringBuffer fileName = new StringBuffer();
- StringBuffer cookie = new StringBuffer();
- StringBuffer cookie2 = new StringBuffer();
- StringBuffer authInfo = new StringBuffer();
- StringBuffer contentType= new StringBuffer();
- StringBuffer contentLocation= new StringBuffer();
-
- Message responseMsg = null;
+ log.info(JavaUtils.getMessage("start00", "SimpleAxisServer",
+ new Integer(getServerSocket().getLocalPort()).toString()));
// Accept and process requests from the socket
while (!stopped) {
Socket socket = null;
-
- // prepare request (do as much as possible while waiting for the
- // next connection). Note the next two statements are commented
- // out. Uncomment them if you experience any problems with not
- // resetting state between requests:
- // msgContext = new MessageContext();
- // requestMsg = new Message("", "String");
try {
- msgContext.setTargetService(null);
- } catch (AxisFault fault) {
- }
- msgContext.setResponseMessage(null);
- msgContext.reset();
- //msgContext.setProperty("transport", "HTTPTransport");
- msgContext.setTransportName(transportName);
-
- responseMsg = null;
-
- try {
- try {
- socket = serverSocket.accept();
- } catch (IOException ioe) {
- break;
- }
-
- // assume the best
- byte[] status = OK;
-
- // assume we're not getting WSDL
- boolean doWsdl = false;
-
- // cookie for this session, if any
- String cooky = null;
-
- try {
- // wipe cookies if we're doing sessions
- if (doSessions) {
- cookie.delete(0, cookie.length());
- cookie2.delete(0, cookie2.length());
- }
- authInfo.delete(0, authInfo.length());
-
- // read headers
- is.setInputStream(socket.getInputStream());
- // parse all headers into hashtable
- int contentLength = parseHeaders(is, contentType,
- contentLocation, soapAction,
- httpRequest, fileName,
- cookie, cookie2, authInfo);
- is.setContentLength(contentLength);
-
- int paramIdx = fileName.toString().indexOf('?');
- if (paramIdx != -1) {
- // Got params
- String params = fileName.substring(paramIdx + 1);
- fileName.setLength(paramIdx);
-
- log.debug(JavaUtils.getMessage("filename00",
- fileName.toString()));
- log.debug(JavaUtils.getMessage("params00",
- params));
-
- if ("wsdl".equalsIgnoreCase(params))
- doWsdl = true;
- }
-
- // Real and relative paths are the same for the
- // SimpleAxisServer
- msgContext.setProperty(Constants.MC_REALPATH,
- fileName.toString());
- msgContext.setProperty(Constants.MC_RELATIVE_PATH,
- fileName.toString());
- msgContext.setProperty(Constants.MC_JWS_CLASSDIR,
- "jwsClasses");
-
- String hostname = socket.getInetAddress().getHostName();
- // !!! Fix string concatenation
- String url = "http://" + hostname + ":" +
- this.getServerSocket().getLocalPort() + "/" +
- fileName.toString();
- msgContext.setProperty(MessageContext.TRANS_URL, url);
-
- String filePart = fileName.toString();
- if (filePart.startsWith("axis/services/")) {
- msgContext.setTargetService(filePart.substring(14));
- }
-
- if (authInfo.length() > 0) {
- // Process authentication info
- //authInfo = new StringBuffer("dXNlcjE6cGFzczE=");
- byte [] decoded = Base64.decode(authInfo.toString());
- StringBuffer userBuf = new StringBuffer();
- StringBuffer pwBuf = new StringBuffer();
- StringBuffer authBuf = userBuf;
- for (int i = 0; i < decoded.length; i++) {
- if ((char)(decoded[i] & 0x7f) == ':') {
- authBuf = pwBuf;
- continue;
- }
- authBuf.append((char)(decoded[i] & 0x7f));
- }
-
- if (log.isDebugEnabled()) {
- log.debug(JavaUtils.getMessage("user00",
- userBuf.toString()));
- }
-
- msgContext.setUsername(userBuf.toString());
- msgContext.setPassword(pwBuf.toString());
- }
-
- // if get, then return simpleton document as response
- if (httpRequest.toString().equals("GET")) {
- OutputStream out = socket.getOutputStream();
- out.write(HTTP);
- out.write(status);
-
- if (doWsdl) {
- engine.generateWSDL(msgContext);
-
- Document doc = (Document)msgContext.getProperty("WSDL");
-
- if (doc != null) {
- String response = XMLUtils.DocumentToString(doc);
- byte [] respBytes = response.getBytes();
-
- out.write(XML_MIME_STUFF);
- putInt(out, respBytes.length);
- out.write(SEPARATOR);
- out.write(respBytes);
- out.flush();
- continue;
- }
- }
-
- out.write(HTML_MIME_STUFF);
- putInt(out, cannedHTMLResponse.length);
- out.write(SEPARATOR);
- out.write(cannedHTMLResponse);
- out.flush();
- continue;
- }
-
- // this may be "" if either SOAPAction: "" or if no SOAPAction
at all.
- // for now, do not complain if no SOAPAction at all
- String soapActionString = soapAction.toString();
- if (soapActionString != null) {
- msgContext.setUseSOAPAction(true);
- msgContext.setSOAPActionURI(soapActionString);
- }
- requestMsg = new Message(is,
- false,
- contentType.toString(),
- contentLocation.toString()
- );
- msgContext.setRequestMessage(requestMsg);
-
- // set up session, if any
- if (doSessions) {
- // did we get a cookie?
- if (cookie.length() > 0) {
- cooky = cookie.toString().trim();
- } else if (cookie2.length() > 0) {
- cooky = cookie2.toString().trim();
- }
-
- // if cooky is null, cook up a cooky
- if (cooky == null) {
- // fake one up!
- // make it be an arbitrarily increasing number
- // (no this is not thread safe because ++ isn't atomic)
- int i = sessionIndex++;
- cooky = "" + i;
- }
-
- // is there a session already?
- Session session = null;
- if (sessions.containsKey(cooky)) {
- session = (Session)sessions.get(cooky);
- } else {
- // no session for this cooky, bummer
- session = new SimpleSession();
-
- // ADD CLEANUP LOGIC HERE if needed
- sessions.put(cooky, session);
- }
-
- msgContext.setSession(session);
- }
-
- // invoke the Axis engine
- engine.invoke(msgContext);
-
- // Retrieve the response from Axis
- responseMsg = msgContext.getResponseMessage();
- if (responseMsg == null) {
- throw new AxisFault(JavaUtils.getMessage("nullResponse00"));
- }
-
- } catch( Exception e ) {
- AxisFault af;
- if (e instanceof AxisFault) {
- af = (AxisFault)e;
- log.debug(JavaUtils.getMessage("serverFault00"), af);
-
- if ("Server.Unauthorized".equals(af.getFaultCode())) {
- status = UNAUTH; // SC_UNAUTHORIZED
- } else {
- status = ISE; // SC_INTERNAL_SERVER_ERROR
- }
- } else {
- status = ISE; // SC_INTERNAL_SERVER_ERROR
- af = AxisFault.makeFault(e);
- }
-
- // There may be headers we want to preserve in the
- // response message - so if it's there, just add the
- // FaultElement to it. Otherwise, make a new one.
- responseMsg = msgContext.getResponseMessage();
- if (responseMsg == null) {
- responseMsg = new Message(af);
- } else {
- try {
- SOAPEnvelope env = responseMsg.getSOAPEnvelope();
- env.clearBody();
- env.addBodyElement(new SOAPFaultElement((AxisFault)e));
- } catch (AxisFault fault) {
- // Should never reach here!
- }
- }
- }
-
-// byte[] response = (byte[]) responseMsg.getSOAPPartAsBytes();
-
- // Send it on its way...
- OutputStream out = socket.getOutputStream();
- out.write(HTTP);
- out.write(status);
- //out.write(XML_MIME_STUFF);
- out.write(("\r\n" + HTTPConstants.HEADER_CONTENT_TYPE + ": " +
responseMsg.getContentType()).getBytes());
- out.write(("\r\n" + HTTPConstants.HEADER_CONTENT_LENGTH + ": " +
responseMsg.getContentLength()).getBytes());
- // putInt(out, response.length);
-
- if (doSessions && null != cooky && 0 != cooky.trim().length()) {
- // write cookie headers, if any
- // don't sweat efficiency *too* badly
- // optimize at will
- StringBuffer cookieOut = new StringBuffer();
- cookieOut.append("\r\nSet-Cookie: ")
- .append(cooky)
- .append("\r\nSet-Cookie2: ")
- .append(cooky);
- // OH, THE HUMILITY! yes this is inefficient.
- out.write(cookieOut.toString().getBytes());
- }
-
- out.write(SEPARATOR);
- responseMsg.writeTo(out);
- // out.write(response);
- out.flush();
-
- if (msgContext.getProperty(msgContext.QUIT_REQUESTED) != null) {
- // why then, quit!
- this.stop();
- }
-
- } catch (InterruptedIOException iie) {
- break;
+ socket = serverSocket.accept();
+ } catch (java.io.InterruptedIOException iie) {
} catch (Exception e) {
log.debug(JavaUtils.getMessage("exception00"), e);
- } finally {
- try {
- if (socket!=null) socket.close();
- } catch (Exception e) {
- }
- }
- }
-
- log.info(JavaUtils.getMessage("quit00", "SimpleAxisServer"));
- }
-
- // ASCII character mapping to lower case
- private static final byte[] toLower = new byte[256];
-
- static {
- for (int i = 0; i < 256; i++) {
- toLower[i] = (byte)i;
- }
-
- for (int lc = 'a'; lc <= 'z'; lc++) {
- toLower[lc + 'A' - 'a']=(byte)lc;
- }
- }
-
- // mime header for content length
- private static final byte lenHeader[] = "content-length: ".getBytes();
- private static final int lenLen = lenHeader.length;
-
- // mime header for content type
- private static final byte typeHeader[] =
(HTTPConstants.HEADER_CONTENT_TYPE.toLowerCase() +": ").getBytes();
- private static final int typeLen = typeHeader.length;
-
- // mime header for content location
- private static final byte locationHeader[] =
(HTTPConstants.HEADER_CONTENT_LOCATION.toLowerCase()+": ").getBytes();
- private static final int locationLen = locationHeader.length;
-
- // mime header for soap action
- private static final byte actionHeader[] = "soapaction: ".getBytes();
- private static final int actionLen = actionHeader.length;
-
- // mime header for cookie
- private static final byte cookieHeader[] = "cookie: ".getBytes();
- private static final int cookieLen = cookieHeader.length;
-
- // mime header for cookie2
- private static final byte cookie2Header[] = "cookie2: ".getBytes();
- private static final int cookie2Len = cookie2Header.length;
-
- // HTTP header for authentication
- private static final byte authHeader[] = "authorization: ".getBytes();
- private static final int authLen = authHeader.length;
-
- // mime header for GET
- private static final byte getHeader[] = "GET".getBytes();
-
- // mime header for POST
- private static final byte postHeader[] = "POST".getBytes();
-
- // header ender
- private static final byte headerEnder[] = ": ".getBytes();
-
- // buffer for IO
- private static final int BUFSIZ = 4096;
- private byte buf[] = new byte[BUFSIZ];
-
- // "Basic" auth string
- private static final byte basicAuth[] = "basic ".getBytes();
-
- /**
- * Read a single line from the input stream
- * @param is inputstream to read from
- * @param b byte array to read into
- * @param off starting offset into the byte array
- * @param len maximum number of bytes to read
- */
- private int readLine(NonBlockingBufferedInputStream is, byte[] b, int off, int
len)
- throws IOException
- {
- int count = 0, c;
-
- while ((c = is.read()) != -1) {
- if(c != '\n' && c != '\r'){
- b[off++] = (byte)c;
- count++;
- }
- if (count == len) break;
- if( '\n' == c ){
- int peek= is.peek(); //If the next line begins with tab or space then
this is a continuation.
- if(peek != ' ' && peek != '\t') break;
- }
- }
- return count > 0 ? count : -1;
- }
-
- /**
- * Read all mime headers, returning the value of Content-Length and
- * SOAPAction.
- * @param is InputStream to read from
- * @param contentType The content type.
- * @param contentLocation The content location
- * @param soapAction StringBuffer to return the soapAction into
- * @param httpRequest StringBuffer for GET / POST
- * @param cookie first cookie header (if doSessions)
- * @param cookie2 second cookie header (if doSessions)
- * @return Content-Length
- */
- private int parseHeaders(NonBlockingBufferedInputStream is,
- StringBuffer contentType,
- StringBuffer contentLocation,
- StringBuffer soapAction,
- StringBuffer httpRequest,
- StringBuffer fileName,
- StringBuffer cookie,
- StringBuffer cookie2,
- StringBuffer authInfo)
- throws IOException
- {
- int n;
- int len = 0;
-
- // parse first line as GET or POST
- n=this.readLine(is, buf, 0, buf.length);
- if (n < 0) {
- // nothing!
- throw new IOException(JavaUtils.getMessage("unexpectedEOS00"));
- }
-
- // which does it begin with?
- httpRequest.delete(0, httpRequest.length());
- fileName.delete(0, fileName.length());
- contentType.delete(0, contentType.length());
- contentLocation.delete(0, contentLocation.length());
-
- if (buf[0] == getHeader[0]) {
- httpRequest.append("GET");
- for (int i = 0; i < n - 5; i++) {
- char c = (char)(buf[i + 5] & 0x7f);
- if (c == ' ')
- break;
- fileName.append(c);
- }
- log.debug( JavaUtils.getMessage("filename01", "SimpleAxisServer",
fileName.toString()));
- return 0;
- } else if (buf[0] == postHeader[0]) {
- httpRequest.append("POST");
- for (int i = 0; i < n - 6; i++) {
- char c = (char)(buf[i + 6] & 0x7f);
- if (c == ' ')
- break;
- fileName.append(c);
- }
- log.debug( JavaUtils.getMessage("filename01", "SimpleAxisServer",
fileName.toString()));
- } else {
- throw new IOException(JavaUtils.getMessage("badRequest00"));
- }
-
- while ((n=readLine(is,buf,0,buf.length)) > 0) {
-
- if ((n<=2) && (buf[0]=='\n'||buf[0]=='\r') && (len>0)) break;
-
- // RobJ gutted the previous logic; it was too hard to extend for more
headers.
- // Now, all it does is search forwards for ": " in the buf,
- // then do a length / byte compare.
- // Hopefully this is still somewhat efficient (Sam is watching!).
-
- // First, search forwards for ": "
- int endHeaderIndex = 0;
- while (endHeaderIndex < n && toLower[buf[endHeaderIndex]] !=
headerEnder[0]) {
- endHeaderIndex++;
- }
- endHeaderIndex += 2;
- // endHeaderIndex now points _just past_ the ": ", and is
- // comparable to the various lenLen, actionLen, etc. values
-
- // convenience; i gets pre-incremented, so initialize it to one less
- int i = endHeaderIndex - 1;
-
- // which header did we find?
- if (endHeaderIndex == lenLen && matches(buf, lenHeader)) {
- // parse content length
-
- while ((++i<n) && (buf[i]>='0') && (buf[i]<='9')) {
- len = (len*10) + (buf[i]-'0');
- }
-
- }
- else if (endHeaderIndex == actionLen
- && matches(buf, actionHeader))
- {
-
- soapAction.delete(0,soapAction.length());
- // skip initial '"'
- i++;
- while ((++i<n) && (buf[i]!='"')) {
- soapAction.append((char)(buf[i] & 0x7f));
- }
-
- }
- else if (doSessions && endHeaderIndex == cookieLen
- && matches(buf, cookieHeader))
- {
-
- // keep everything up to first ;
- while ((++i<n) && (buf[i]!=';') && (buf[i]!='\r') &&
(buf[i]!='\n')) {
- cookie.append((char)(buf[i] & 0x7f));
- }
-
- }
- else if (doSessions && endHeaderIndex == cookie2Len
- && matches(buf, cookie2Header))
- {
-
- // keep everything up to first ;
- while ((++i<n) && (buf[i]!=';') && (buf[i]!='\r') &&
(buf[i]!='\n')) {
- cookie2.append((char)(buf[i] & 0x7f));
- }
-
- }
- else if (endHeaderIndex == authLen && matches(buf, authHeader)) {
- if (matches(buf, endHeaderIndex, basicAuth)) {
- i += basicAuth.length;
- while (++i<n && (buf[i]!='\r') && (buf[i]!='\n')) {
- if (buf[i]==' ') continue;
- authInfo.append((char)(buf[i] & 0x7f));
- }
- } else {
- throw new IOException(
- JavaUtils.getMessage("badAuth00"));
- }
- }
- else if (endHeaderIndex == locationLen && matches(buf, locationHeader))
{
- while (++i<n && (buf[i]!='\r') && (buf[i]!='\n')) {
- if (buf[i]==' ') continue;
- contentLocation.append((char)(buf[i] & 0x7f));
- }
- }
- else if (endHeaderIndex == typeLen&& matches(buf, typeHeader)) {
- while (++i<n && (buf[i]!='\r') && (buf[i]!='\n')) {
- if (buf[i]==' ') continue;
- contentType.append((char)(buf[i] & 0x7f));
- }
- }
-
- }
- return len;
- }
-
-
- /**
- * does tolower[buf] match the target byte array, up to the target's length?
- */
- public boolean matches (byte[] buf, byte[] target) {
- for (int i = 0; i < target.length; i++) {
- if (toLower[buf[i]] != target[i]) {
- return false;
+ break;
}
- }
- return true;
- }
-
- /**
- * Case-insensitive match of a target byte [] to a source byte [],
- * starting from a particular offset into the source.
- */
- public boolean matches (byte[] buf, int bufIdx, byte[] target) {
- for (int i = 0; i < target.length; i++) {
- if (toLower[buf[bufIdx + i]] != target[i]) {
- return false;
+ if (socket != null) {
+ SimpleAxisWorker worker = new SimpleAxisWorker(this, socket);
+ Thread thread = new Thread(worker);
+ thread.setDaemon(true);
+ thread.start();
}
}
- return true;
- }
-
-
- /**
- * output an integer into the output stream
- * @param out OutputStream to be written to
- * @param value Integer value to be written.
- */
- private void putInt(OutputStream out, int value)
- throws IOException
- {
- int len = 0;
- int offset=buf.length;
-
- // negative numbers
- if (value < 0) {
- buf[--offset] = (byte) '-';
- value=-value;
- len++;
- }
-
- // zero
- if (value == 0) {
- buf[--offset] = (byte) '0';
- len++;
- }
-
- // positive numbers
- while (value > 0) {
- buf[--offset] = (byte)(value%10 + '0');
- value=value/10;
- len++;
- }
-
- // write the result
- out.write(buf, offset, len);
+ log.info(JavaUtils.getMessage("quit00", "SimpleAxisServer"));
}
// per thread socket information
@@ -825,6 +209,11 @@
*/
public void stop() throws Exception {
stopped = true;
+ try {
+ serverSocket.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
if (worker != null) worker.interrupt();
}
@@ -848,12 +237,11 @@
int port = opts.getPort();
ServerSocket ss = new ServerSocket(port);
sas.setServerSocket(ss);
- sas.run();
+ sas.start();
} catch (Exception e) {
log.error(JavaUtils.getMessage("exception00"), e);
return;
}
- }
-
+ }
}
1.1
xml-axis/java/src/org/apache/axis/transport/http/SimpleAxisWorker.java
Index: SimpleAxisWorker.java
===================================================================
/*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Axis" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact [EMAIL PROTECTED]
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
package org.apache.axis.transport.http;
import org.apache.axis.AxisFault;
import org.apache.axis.Constants;
import org.apache.axis.Message;
import org.apache.axis.MessageContext;
import org.apache.axis.encoding.Base64;
import org.apache.axis.message.SOAPEnvelope;
import org.apache.axis.message.SOAPFaultElement;
import org.apache.axis.server.AxisServer;
import org.apache.axis.utils.JavaUtils;
import org.apache.axis.utils.XMLUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Document;
import java.io.OutputStream;
import java.net.Socket;
public class SimpleAxisWorker implements Runnable {
protected static Log log =
LogFactory.getLog(SimpleAxisWorker.class.getName());
private SimpleAxisServer server;
private Socket socket;
// Axis specific constants
private static String transportName = "SimpleHTTP";
// HTTP status codes
private static byte OK[] = ("200 " + JavaUtils.getMessage("ok00")).getBytes();
private static byte UNAUTH[] = ("401 " +
JavaUtils.getMessage("unauth00")).getBytes();
private static byte ISE[] = ("500 " +
JavaUtils.getMessage("internalError01")).getBytes();
// HTTP prefix
private static byte HTTP[] = "HTTP/1.0 ".getBytes();
// Standard MIME headers for XML payload
private static byte XML_MIME_STUFF[] =
("\r\nContent-Type: text/xml; charset=utf-8\r\n" +
"Content-Length: ").getBytes();
// Standard MIME headers for HTML payload
private static byte HTML_MIME_STUFF[] =
("\r\nContent-Type: text/html; charset=utf-8\r\n" +
"Content-Length: ").getBytes();
// Mime/Content separator
private static byte SEPARATOR[] = "\r\n\r\n".getBytes();
// Tiddly little response
private static final String responseStr =
"<html><head><title>SimpleAxisServer</title></head>" +
"<body><h1>SimpleAxisServer</h1>" +
JavaUtils.getMessage("reachedServer00") +
"</html>";
private static byte cannedHTMLResponse[] = responseStr.getBytes();
// ASCII character mapping to lower case
private static final byte[] toLower = new byte[256];
static {
for (int i = 0; i < 256; i++) {
toLower[i] = (byte) i;
}
for (int lc = 'a'; lc <= 'z'; lc++) {
toLower[lc + 'A' - 'a'] = (byte) lc;
}
}
// buffer for IO
private static final int BUFSIZ = 4096;
// mime header for content length
private static final byte lenHeader[] = "content-length: ".getBytes();
private static final int lenLen = lenHeader.length;
// mime header for content type
private static final byte typeHeader[] =
(HTTPConstants.HEADER_CONTENT_TYPE.toLowerCase() + ": ").getBytes();
private static final int typeLen = typeHeader.length;
// mime header for content location
private static final byte locationHeader[] =
(HTTPConstants.HEADER_CONTENT_LOCATION.toLowerCase() + ": ").getBytes();
private static final int locationLen = locationHeader.length;
// mime header for soap action
private static final byte actionHeader[] = "soapaction: ".getBytes();
private static final int actionLen = actionHeader.length;
// mime header for cookie
private static final byte cookieHeader[] = "cookie: ".getBytes();
private static final int cookieLen = cookieHeader.length;
// mime header for cookie2
private static final byte cookie2Header[] = "cookie2: ".getBytes();
private static final int cookie2Len = cookie2Header.length;
// HTTP header for authentication
private static final byte authHeader[] = "authorization: ".getBytes();
private static final int authLen = authHeader.length;
// mime header for GET
private static final byte getHeader[] = "GET".getBytes();
// mime header for POST
private static final byte postHeader[] = "POST".getBytes();
// header ender
private static final byte headerEnder[] = ": ".getBytes();
// "Basic" auth string
private static final byte basicAuth[] = "basic ".getBytes();
public SimpleAxisWorker(SimpleAxisServer server, Socket socket) {
this.server = server;
this.socket = socket;
}
/**
* The main workhorse method.
*/
public void run() {
byte buf[] = new byte[BUFSIZ];
// create an Axis server
AxisServer engine = server.getAxisServer();
// create and initialize a message context
MessageContext msgContext = new MessageContext(engine);
Message requestMsg;
// Reusuable, buffered, content length controlled, InputStream
NonBlockingBufferedInputStream is =
new NonBlockingBufferedInputStream();
// buffers for the headers we care about
StringBuffer soapAction = new StringBuffer();
StringBuffer httpRequest = new StringBuffer();
StringBuffer fileName = new StringBuffer();
StringBuffer cookie = new StringBuffer();
StringBuffer cookie2 = new StringBuffer();
StringBuffer authInfo = new StringBuffer();
StringBuffer contentType = new StringBuffer();
StringBuffer contentLocation = new StringBuffer();
Message responseMsg = null;
// prepare request (do as much as possible while waiting for the
// next connection). Note the next two statements are commented
// out. Uncomment them if you experience any problems with not
// resetting state between requests:
// msgContext = new MessageContext();
// requestMsg = new Message("", "String");
try {
msgContext.setTargetService(null);
} catch (AxisFault fault) {
}
msgContext.setResponseMessage(null);
msgContext.reset();
//msgContext.setProperty("transport", "HTTPTransport");
msgContext.setTransportName(transportName);
responseMsg = null;
try {
// assume the best
byte[] status = OK;
// assume we're not getting WSDL
boolean doWsdl = false;
// cookie for this session, if any
String cooky = null;
try {
// wipe cookies if we're doing sessions
if (server.isSessionUsed()) {
cookie.delete(0, cookie.length());
cookie2.delete(0, cookie2.length());
}
authInfo.delete(0, authInfo.length());
// read headers
is.setInputStream(socket.getInputStream());
// parse all headers into hashtable
int contentLength = parseHeaders(is, buf, contentType,
contentLocation, soapAction,
httpRequest, fileName,
cookie, cookie2, authInfo);
is.setContentLength(contentLength);
int paramIdx = fileName.toString().indexOf('?');
if (paramIdx != -1) {
// Got params
String params = fileName.substring(paramIdx + 1);
fileName.setLength(paramIdx);
log.debug(JavaUtils.getMessage("filename00",
fileName.toString()));
log.debug(JavaUtils.getMessage("params00",
params));
if ("wsdl".equalsIgnoreCase(params))
doWsdl = true;
}
// Real and relative paths are the same for the
// SimpleAxisServer
msgContext.setProperty(Constants.MC_REALPATH,
fileName.toString());
msgContext.setProperty(Constants.MC_RELATIVE_PATH,
fileName.toString());
msgContext.setProperty(Constants.MC_JWS_CLASSDIR,
"jwsClasses");
String hostname = socket.getInetAddress().getHostName();
// !!! Fix string concatenation
String url = "http://" + hostname + ":" +
server.getServerSocket().getLocalPort() + "/" +
fileName.toString();
msgContext.setProperty(MessageContext.TRANS_URL, url);
String filePart = fileName.toString();
if (filePart.startsWith("axis/services/")) {
msgContext.setTargetService(filePart.substring(14));
}
if (authInfo.length() > 0) {
// Process authentication info
//authInfo = new StringBuffer("dXNlcjE6cGFzczE=");
byte[] decoded = Base64.decode(authInfo.toString());
StringBuffer userBuf = new StringBuffer();
StringBuffer pwBuf = new StringBuffer();
StringBuffer authBuf = userBuf;
for (int i = 0; i < decoded.length; i++) {
if ((char) (decoded[i] & 0x7f) == ':') {
authBuf = pwBuf;
continue;
}
authBuf.append((char) (decoded[i] & 0x7f));
}
if (log.isDebugEnabled()) {
log.debug(JavaUtils.getMessage("user00",
userBuf.toString()));
}
msgContext.setUsername(userBuf.toString());
msgContext.setPassword(pwBuf.toString());
}
// if get, then return simpleton document as response
if (httpRequest.toString().equals("GET")) {
OutputStream out = socket.getOutputStream();
out.write(HTTP);
out.write(status);
if (doWsdl) {
engine.generateWSDL(msgContext);
Document doc = (Document) msgContext.getProperty("WSDL");
if (doc != null) {
String response = XMLUtils.DocumentToString(doc);
byte[] respBytes = response.getBytes();
out.write(XML_MIME_STUFF);
putInt(buf, out, respBytes.length);
out.write(SEPARATOR);
out.write(respBytes);
out.flush();
return;
}
}
out.write(HTML_MIME_STUFF);
putInt(buf, out, cannedHTMLResponse.length);
out.write(SEPARATOR);
out.write(cannedHTMLResponse);
out.flush();
return;
}
// this may be "" if either SOAPAction: "" or if no SOAPAction at
all.
// for now, do not complain if no SOAPAction at all
String soapActionString = soapAction.toString();
if (soapActionString != null) {
msgContext.setUseSOAPAction(true);
msgContext.setSOAPActionURI(soapActionString);
}
requestMsg = new Message(is,
false,
contentType.toString(),
contentLocation.toString()
);
msgContext.setRequestMessage(requestMsg);
// set up session, if any
if (server.isSessionUsed()) {
// did we get a cookie?
if (cookie.length() > 0) {
cooky = cookie.toString().trim();
} else if (cookie2.length() > 0) {
cooky = cookie2.toString().trim();
}
// if cooky is null, cook up a cooky
if (cooky == null) {
// fake one up!
// make it be an arbitrarily increasing number
// (no this is not thread safe because ++ isn't atomic)
int i = server.sessionIndex++;
cooky = "" + i;
}
msgContext.setSession(server.createSession(cooky));
}
// invoke the Axis engine
engine.invoke(msgContext);
// Retrieve the response from Axis
responseMsg = msgContext.getResponseMessage();
if (responseMsg == null) {
throw new AxisFault(JavaUtils.getMessage("nullResponse00"));
}
} catch (Exception e) {
AxisFault af;
if (e instanceof AxisFault) {
af = (AxisFault) e;
log.debug(JavaUtils.getMessage("serverFault00"), af);
if ("Server.Unauthorized".equals(af.getFaultCode())) {
status = UNAUTH; // SC_UNAUTHORIZED
} else {
status = ISE; // SC_INTERNAL_SERVER_ERROR
}
} else {
status = ISE; // SC_INTERNAL_SERVER_ERROR
af = AxisFault.makeFault(e);
}
// There may be headers we want to preserve in the
// response message - so if it's there, just add the
// FaultElement to it. Otherwise, make a new one.
responseMsg = msgContext.getResponseMessage();
if (responseMsg == null) {
responseMsg = new Message(af);
} else {
try {
SOAPEnvelope env = responseMsg.getSOAPEnvelope();
env.clearBody();
env.addBodyElement(new SOAPFaultElement((AxisFault) e));
} catch (AxisFault fault) {
// Should never reach here!
}
}
}
// Send it on its way...
OutputStream out = socket.getOutputStream();
out.write(HTTP);
out.write(status);
//out.write(XML_MIME_STUFF);
out.write(("\r\n" + HTTPConstants.HEADER_CONTENT_TYPE + ": " +
responseMsg.getContentType()).getBytes());
out.write(("\r\n" + HTTPConstants.HEADER_CONTENT_LENGTH + ": " +
responseMsg.getContentLength()).getBytes());
// putInt(out, response.length);
if (server.isSessionUsed() && null != cooky && 0 !=
cooky.trim().length()) {
// write cookie headers, if any
// don't sweat efficiency *too* badly
// optimize at will
StringBuffer cookieOut = new StringBuffer();
cookieOut.append("\r\nSet-Cookie: ")
.append(cooky)
.append("\r\nSet-Cookie2: ")
.append(cooky);
// OH, THE HUMILITY! yes this is inefficient.
out.write(cookieOut.toString().getBytes());
}
out.write(SEPARATOR);
responseMsg.writeTo(out);
// out.write(response);
out.flush();
if (msgContext.getProperty(msgContext.QUIT_REQUESTED) != null) {
// why then, quit!
server.stop();
}
} catch (Exception e) {
log.debug(JavaUtils.getMessage("exception00"), e);
} finally {
try {
if (socket != null) socket.close();
} catch (Exception e) {
}
}
}
/**
* Read all mime headers, returning the value of Content-Length and
* SOAPAction.
* @param is InputStream to read from
* @param contentType The content type.
* @param contentLocation The content location
* @param soapAction StringBuffer to return the soapAction into
* @param httpRequest StringBuffer for GET / POST
* @param cookie first cookie header (if doSessions)
* @param cookie2 second cookie header (if doSessions)
* @return Content-Length
*/
private int parseHeaders(NonBlockingBufferedInputStream is,
byte buf[],
StringBuffer contentType,
StringBuffer contentLocation,
StringBuffer soapAction,
StringBuffer httpRequest,
StringBuffer fileName,
StringBuffer cookie,
StringBuffer cookie2,
StringBuffer authInfo)
throws java.io.IOException {
int n;
int len = 0;
// parse first line as GET or POST
n = this.readLine(is, buf, 0, buf.length);
if (n < 0) {
// nothing!
throw new java.io.IOException(JavaUtils.getMessage("unexpectedEOS00"));
}
// which does it begin with?
httpRequest.delete(0, httpRequest.length());
fileName.delete(0, fileName.length());
contentType.delete(0, contentType.length());
contentLocation.delete(0, contentLocation.length());
if (buf[0] == getHeader[0]) {
httpRequest.append("GET");
for (int i = 0; i < n - 5; i++) {
char c = (char) (buf[i + 5] & 0x7f);
if (c == ' ')
break;
fileName.append(c);
}
log.debug(JavaUtils.getMessage("filename01", "SimpleAxisServer",
fileName.toString()));
return 0;
} else if (buf[0] == postHeader[0]) {
httpRequest.append("POST");
for (int i = 0; i < n - 6; i++) {
char c = (char) (buf[i + 6] & 0x7f);
if (c == ' ')
break;
fileName.append(c);
}
log.debug(JavaUtils.getMessage("filename01", "SimpleAxisServer",
fileName.toString()));
} else {
throw new java.io.IOException(JavaUtils.getMessage("badRequest00"));
}
while ((n = readLine(is, buf, 0, buf.length)) > 0) {
if ((n <= 2) && (buf[0] == '\n' || buf[0] == '\r') && (len > 0)) break;
// RobJ gutted the previous logic; it was too hard to extend for more
headers.
// Now, all it does is search forwards for ": " in the buf,
// then do a length / byte compare.
// Hopefully this is still somewhat efficient (Sam is watching!).
// First, search forwards for ": "
int endHeaderIndex = 0;
while (endHeaderIndex < n && toLower[buf[endHeaderIndex]] !=
headerEnder[0]) {
endHeaderIndex++;
}
endHeaderIndex += 2;
// endHeaderIndex now points _just past_ the ": ", and is
// comparable to the various lenLen, actionLen, etc. values
// convenience; i gets pre-incremented, so initialize it to one less
int i = endHeaderIndex - 1;
// which header did we find?
if (endHeaderIndex == lenLen && matches(buf, lenHeader)) {
// parse content length
while ((++i < n) && (buf[i] >= '0') && (buf[i] <= '9')) {
len = (len * 10) + (buf[i] - '0');
}
} else if (endHeaderIndex == actionLen
&& matches(buf, actionHeader)) {
soapAction.delete(0, soapAction.length());
// skip initial '"'
i++;
while ((++i < n) && (buf[i] != '"')) {
soapAction.append((char) (buf[i] & 0x7f));
}
} else if (server.isSessionUsed() && endHeaderIndex == cookieLen
&& matches(buf, cookieHeader)) {
// keep everything up to first ;
while ((++i < n) && (buf[i] != ';') && (buf[i] != '\r') && (buf[i]
!= '\n')) {
cookie.append((char) (buf[i] & 0x7f));
}
} else if (server.isSessionUsed() && endHeaderIndex == cookie2Len
&& matches(buf, cookie2Header)) {
// keep everything up to first ;
while ((++i < n) && (buf[i] != ';') && (buf[i] != '\r') && (buf[i]
!= '\n')) {
cookie2.append((char) (buf[i] & 0x7f));
}
} else if (endHeaderIndex == authLen && matches(buf, authHeader)) {
if (matches(buf, endHeaderIndex, basicAuth)) {
i += basicAuth.length;
while (++i < n && (buf[i] != '\r') && (buf[i] != '\n')) {
if (buf[i] == ' ') continue;
authInfo.append((char) (buf[i] & 0x7f));
}
} else {
throw new java.io.IOException(
JavaUtils.getMessage("badAuth00"));
}
} else if (endHeaderIndex == locationLen && matches(buf,
locationHeader)) {
while (++i < n && (buf[i] != '\r') && (buf[i] != '\n')) {
if (buf[i] == ' ') continue;
contentLocation.append((char) (buf[i] & 0x7f));
}
} else if (endHeaderIndex == typeLen && matches(buf, typeHeader)) {
while (++i < n && (buf[i] != '\r') && (buf[i] != '\n')) {
if (buf[i] == ' ') continue;
contentType.append((char) (buf[i] & 0x7f));
}
}
}
return len;
}
/**
* does tolower[buf] match the target byte array, up to the target's length?
*/
public boolean matches(byte[] buf, byte[] target) {
for (int i = 0; i < target.length; i++) {
if (toLower[buf[i]] != target[i]) {
return false;
}
}
return true;
}
/**
* Case-insensitive match of a target byte [] to a source byte [],
* starting from a particular offset into the source.
*/
public boolean matches(byte[] buf, int bufIdx, byte[] target) {
for (int i = 0; i < target.length; i++) {
if (toLower[buf[bufIdx + i]] != target[i]) {
return false;
}
}
return true;
}
/**
* output an integer into the output stream
* @param out OutputStream to be written to
* @param value Integer value to be written.
*/
private void putInt(byte buf[], OutputStream out, int value)
throws java.io.IOException {
int len = 0;
int offset = buf.length;
// negative numbers
if (value < 0) {
buf[--offset] = (byte) '-';
value = -value;
len++;
}
// zero
if (value == 0) {
buf[--offset] = (byte) '0';
len++;
}
// positive numbers
while (value > 0) {
buf[--offset] = (byte) (value % 10 + '0');
value = value / 10;
len++;
}
// write the result
out.write(buf, offset, len);
}
/**
* Read a single line from the input stream
* @param is inputstream to read from
* @param b byte array to read into
* @param off starting offset into the byte array
* @param len maximum number of bytes to read
*/
private int readLine(NonBlockingBufferedInputStream is, byte[] b, int off, int
len)
throws java.io.IOException {
int count = 0, c;
while ((c = is.read()) != -1) {
if (c != '\n' && c != '\r') {
b[off++] = (byte) c;
count++;
}
if (count == len) break;
if ('\n' == c) {
int peek = is.peek(); //If the next line begins with tab or space
then this is a continuation.
if (peek != ' ' && peek != '\t') break;
}
}
return count > 0 ? count : -1;
}
}