We are still heading to the "Not-So-SimpleAxisServer" that Alan was talking about a while ago. He volunteered to integrate Jetty optionally in to our kit, which *I* thought was a pretty good idea, since we would be using AxisServlet for testing and we could stop using SimpleAxisServer for much of anything.
Alan, are you still interesting is doing this work? Can we get some votes on integrating Jetty in to Axis to encourage Alan that his work will be submitted? As for Dims work here, +1 to making things better! :-) -- Tom Jordahl Macromedia -----Original Message----- From: Glen Daniels [mailto:[EMAIL PROTECTED]] Sent: Friday, June 07, 2002 4:21 PM To: '[EMAIL PROTECTED]' Subject: RE: cvs commit: xml-axis/java/src/org/apache/axis/transport/http SimpleAxisWorker.java SimpleAxisServer.java *applause* :) > -----Original Message----- > From: [EMAIL PROTECTED] [mailto:[EMAIL PROTECTED]] > Sent: Friday, June 07, 2002 2:32 PM > To: [EMAIL PROTECTED] > Subject: cvs commit: xml-axis/java/src/org/apache/axis/transport/http > SimpleAxisWorker.java SimpleAxisServer.java > > > 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/MultithreadTestC > ase.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/Sim > pleAxisServer.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; > } > } > > > >