costin      01/06/26 12:52:26

  Modified:    jk/java/org/apache/ajp Ajp14.java Ajp14Packet.java
               jk/java/org/apache/ajp/tomcat33 Ajp14Interceptor.java
  Log:
  Merged ( copied ) the code from Ajp13 into Ajp14, removed "extend Ajp13".
  
  The main reason is that we need to keep Ajp13 stable while we want to
  do some refactoring in Ajp14 - separate the marshaling, add an extension
  mechanism to be used to implement and add new callbacks, drop Ajp14Packet
  and start using the ByteChunk.
  
  In other words the architecture will change from "Ajp14 extends Ajp13" into
  a AjpMarshaling ( which will work for both ), and AjpCallback ( with
  "original" 13 callbacks plus new ajp14 callbacks ), i.e. composition instead of
  inheritance.
  
  Revision  Changes    Path
  1.2       +641 -24   jakarta-tomcat-connectors/jk/java/org/apache/ajp/Ajp14.java
  
  Index: Ajp14.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat-connectors/jk/java/org/apache/ajp/Ajp14.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- Ajp14.java        2001/06/24 20:44:49     1.1
  +++ Ajp14.java        2001/06/26 19:52:17     1.2
  @@ -67,10 +67,8 @@
   import java.util.Enumeration;
   import java.security.*;
   
  -import org.apache.tomcat.util.http.MimeHeaders;
  -import org.apache.tomcat.util.buf.MessageBytes;
  -import org.apache.tomcat.util.http.HttpMessages;
  -import org.apache.tomcat.util.buf.HexUtils;
  +import org.apache.tomcat.util.http.*;
  +import org.apache.tomcat.util.buf.*;
   
   
   /**
  @@ -92,7 +90,7 @@
    * @author Dan Milstein [[EMAIL PROTECTED]]
    * @author Keith Wannamaker [[EMAIL PROTECTED]]
    */
  -public class Ajp14 extends Ajp13
  +public class Ajp14
   {
       // Original AJP13 commands    
       // Prefix codes for message types from server to container
  @@ -207,12 +205,118 @@
       public static final byte AJP14_CONTEXT_DOWN                       = 0x01;
       public static final byte AJP14_CONTEXT_UP                         = 0x02;
       public static final byte AJP14_CONTEXT_OK                         = 0x03;
  +
       
  +    public static final int MAX_PACKET_SIZE=8192;
  +    public static final int H_SIZE=4;  // Size of basic packet header
  +
  +    public static final int  MAX_READ_SIZE = MAX_PACKET_SIZE - H_SIZE - 2;
  +    public static final int  MAX_SEND_SIZE = MAX_PACKET_SIZE - H_SIZE - 4;
  +
  +    // Integer codes for common response header strings
  +    public static final int SC_RESP_CONTENT_TYPE        = 0xA001;
  +    public static final int SC_RESP_CONTENT_LANGUAGE    = 0xA002;
  +    public static final int SC_RESP_CONTENT_LENGTH      = 0xA003;
  +    public static final int SC_RESP_DATE                = 0xA004;
  +    public static final int SC_RESP_LAST_MODIFIED       = 0xA005;
  +    public static final int SC_RESP_LOCATION            = 0xA006;
  +    public static final int SC_RESP_SET_COOKIE          = 0xA007;
  +    public static final int SC_RESP_SET_COOKIE2         = 0xA008;
  +    public static final int SC_RESP_SERVLET_ENGINE      = 0xA009;
  +    public static final int SC_RESP_STATUS              = 0xA00A;
  +    public static final int SC_RESP_WWW_AUTHENTICATE    = 0xA00B;
  +     
  +    // Integer codes for common (optional) request attribute names
  +    public static final byte SC_A_CONTEXT       = 1;  // XXX Unused
  +    public static final byte SC_A_SERVLET_PATH  = 2;  // XXX Unused
  +    public static final byte SC_A_REMOTE_USER   = 3;
  +    public static final byte SC_A_AUTH_TYPE     = 4;
  +    public static final byte SC_A_QUERY_STRING  = 5;
  +    public static final byte SC_A_JVM_ROUTE     = 6;
  +    public static final byte SC_A_SSL_CERT      = 7;
  +    public static final byte SC_A_SSL_CIPHER    = 8;
  +    public static final byte SC_A_SSL_SESSION   = 9;
  +
  +    // Used for attributes which are not in the list above
  +    public static final byte SC_A_REQ_ATTRIBUTE = 10; 
  +
  +    // Terminates list of attributes
  +    public static final byte SC_A_ARE_DONE      = (byte)0xFF;
  +    
  +    // Translates integer codes to names of HTTP methods
  +    public static final String []methodTransArray = {
  +        "OPTIONS",
  +        "GET",
  +        "HEAD",
  +        "POST",
  +        "PUT",
  +        "DELETE",
  +        "TRACE",
  +        "PROPFIND",
  +        "PROPPATCH",
  +        "MKCOL",
  +        "COPY",
  +        "MOVE",
  +        "LOCK",
  +        "UNLOCK",
  +        "ACL"
  +    };
  +    
  +    // id's for common request headers
  +    public static final int SC_REQ_ACCEPT          = 1;
  +    public static final int SC_REQ_ACCEPT_CHARSET  = 2;
  +    public static final int SC_REQ_ACCEPT_ENCODING = 3;
  +    public static final int SC_REQ_ACCEPT_LANGUAGE = 4;
  +    public static final int SC_REQ_AUTHORIZATION   = 5;
  +    public static final int SC_REQ_CONNECTION      = 6;
  +    public static final int SC_REQ_CONTENT_TYPE    = 7;
  +    public static final int SC_REQ_CONTENT_LENGTH  = 8;
  +    public static final int SC_REQ_COOKIE          = 9;
  +    public static final int SC_REQ_COOKIE2         = 10;
  +    public static final int SC_REQ_HOST            = 11;
  +    public static final int SC_REQ_PRAGMA          = 12;
  +    public static final int SC_REQ_REFERER         = 13;
  +    public static final int SC_REQ_USER_AGENT      = 14;
       // AJP14 new header
  -    public static final byte SC_A_SSL_KEY_SIZE  = 11;
  +    public static final byte SC_A_SSL_KEY_SIZE  = 11; // XXX ??? 
   
  -    // ============ Instance Properties ====================
  +    // Translates integer codes to request header names    
  +    public static final String []headerTransArray = {
  +        "accept",
  +        "accept-charset",
  +        "accept-encoding",
  +        "accept-language",
  +        "authorization",
  +        "connection",
  +        "content-type",
  +        "content-length",
  +        "cookie",
  +        "cookie2",
  +        "host",
  +        "pragma",
  +        "referer",
  +        "user-agent"
  +    };
   
  +    
  +    // ============ Instance Properties ====================
  +    // Ajp13
  +    OutputStream out;
  +    InputStream in;
  +
  +    // Buffer used of output body and headers
  +    Ajp14Packet outBuf = new Ajp14Packet( MAX_PACKET_SIZE );
  +    // Buffer used for input body
  +    Ajp14Packet inBuf  = new Ajp14Packet( MAX_PACKET_SIZE );
  +    // Buffer used for request head ( and headers )
  +    Ajp14Packet hBuf=new Ajp14Packet( MAX_PACKET_SIZE );
  +    
  +    // Holds incoming reads of request body data (*not* header data)
  +    byte []bodyBuff = new byte[MAX_READ_SIZE];
  +    
  +    int blen;  // Length of current chunk of body data in buffer
  +    int pos;   // Current read position within that buffer
  +    
       // AJP14 
       boolean logged = false;
       int          webserverNegociation        = 0;
  @@ -239,6 +343,30 @@
        hBuf   = new Ajp14Packet( MAX_PACKET_SIZE );
       }
   
  +    public void recycle() {
  +        if (debug > 0) {
  +            log("recycle()");
  +        }
  +
  +        // This is a touch cargo-cultish, but I think wise.
  +        blen = 0; 
  +        pos = 0;
  +    }
  +    
  +    /**
  +     * Associate an open socket with this instance.
  +     */
  +    public void setSocket( Socket socket ) throws IOException {
  +        if (debug > 0) {
  +            log("setSocket()");
  +        }
  +        
  +     socket.setSoLinger( true, 100);
  +     out = socket.getOutputStream();
  +     in  = socket.getInputStream();
  +     pos = 0;
  +    }
  +
       // -------------------- Parameters --------------------
       
       /**
  @@ -288,7 +416,7 @@
        * if there were errors in the reading of the request, and -2 if the
        * server is asking the container to shut itself down.  
        */
  -    public int receiveNextRequest(AjpRequest req) throws IOException 
  +    public int receiveNextRequest(BaseRequest req) throws IOException 
       {
        // XXX The return values are awful.
        int err = receive(hBuf);
  @@ -379,6 +507,184 @@
       }
   
       //-------------------- Implementation for various protocol commands 
--------------------
  +
  +    /**
  +     * Try to decode Headers - AJP13 will do nothing but descendant will     
  +     * override this method to handle new headers (ie SSL_KEY_SIZE in AJP14)
  +     */
  +    int decodeMoreHeaders(BaseRequest req, byte attribute, Ajp14Packet msg)
  +    {
  +     if (attribute == SC_A_SSL_KEY_SIZE) {
  +         req.setAttribute("javax.servlet.request.key_size", 
Integer.toString(msg.getInt()));
  +         return 200;
  +     }
  +     return 500; // Error
  +    }
  +    
  +    /**
  +     * Parse a FORWARD_REQUEST packet from the web server and store its
  +     * properties in the passed-in request object.
  +     *
  +     * @param req An empty (newly-recycled) request object.
  +     * @param msg Holds the packet which has just been sent by the web
  +     * server, with its read position just past the packet header (which in
  +     * this case includes the prefix code for FORWARD_REQUEST).
  +     *
  +     * @return 200 in case of a successful decoduing, 500 in case of error.  
  +     */
  +    protected int decodeRequest(BaseRequest req, Ajp14Packet msg)
  +        throws IOException
  +    {
  +        
  +        if (debug > 0) {
  +            log("decodeRequest()");
  +        }
  +
  +     // XXX Awful return values
  +
  +        boolean isSSL = false;
  +
  +        // Translate the HTTP method code to a String.
  +        byte methodCode = msg.getByte();
  +        req.method().setString(methodTransArray[(int)methodCode - 1]);
  +
  +        msg.getMessageBytes(req.protocol()); 
  +        msg.getMessageBytes(req.requestURI());
  +
  +        msg.getMessageBytes(req.remoteAddr());
  +        msg.getMessageBytes(req.remoteHost());
  +        msg.getMessageBytes(req.serverName());
  +        req.setServerPort(msg.getInt());
  +
  +     isSSL = msg.getBool();
  +
  +     // Decode headers
  +     MimeHeaders headers = req.headers();
  +     int hCount = msg.getInt();
  +        for(int i = 0 ; i < hCount ; i++) {
  +            String hName = null;
  +
  +         // Header names are encoded as either an integer code starting
  +         // with 0xA0, or as a normal string (in which case the first
  +         // two bytes are the length).
  +            int isc = msg.peekInt();
  +            int hId = isc & 0xFF;
  +
  +         MessageBytes vMB=null;
  +            isc &= 0xFF00;
  +            if(0xA000 == isc) {
  +                msg.getInt(); // To advance the read position
  +                hName = headerTransArray[hId - 1];
  +             vMB= headers.addValue(hName);
  +            } else {
  +             // XXX Not very elegant
  +             vMB = msg.addHeader(headers);
  +             if (vMB == null) {
  +                    return 500; // wrong packet
  +                }
  +            }
  +
  +            msg.getMessageBytes(vMB);
  +
  +            // set content length, if this is it...
  +            if (hId == SC_REQ_CONTENT_LENGTH) {
  +                int contentLength = (vMB == null) ? -1 : vMB.getInt();
  +                req.setContentLength(contentLength);
  +            } else if (hId == SC_REQ_CONTENT_TYPE) {
  +                ByteChunk bchunk = vMB.getByteChunk();
  +                req.contentType().setBytes(bchunk.getBytes(),
  +                                           bchunk.getOffset(),
  +                                           bchunk.getLength());
  +            }
  +        }
  +
  +     byte attributeCode;
  +        for(attributeCode = msg.getByte() ;
  +            attributeCode != SC_A_ARE_DONE ;
  +            attributeCode = msg.getByte()) {
  +            switch(attributeCode) {
  +         case SC_A_CONTEXT      :
  +                break;
  +             
  +         case SC_A_SERVLET_PATH :
  +                break;
  +             
  +         case SC_A_REMOTE_USER  :
  +                msg.getMessageBytes(req.remoteUser());
  +                break;
  +             
  +         case SC_A_AUTH_TYPE    :
  +                msg.getMessageBytes(req.authType());
  +                break;
  +             
  +         case SC_A_QUERY_STRING :
  +             msg.getMessageBytes(req.queryString());
  +                break;
  +             
  +         case SC_A_JVM_ROUTE    :
  +                msg.getMessageBytes(req.jvmRoute());
  +                break;
  +             
  +         case SC_A_SSL_CERT     :
  +             isSSL = true;
  +             req.setAttribute("javax.servlet.request.X509Certificate",
  +                              msg.getString());
  +                break;
  +             
  +         case SC_A_SSL_CIPHER   :
  +             isSSL = true;
  +             req.setAttribute("javax.servlet.request.cipher_suite",
  +                              msg.getString());
  +                break;
  +             
  +         case SC_A_SSL_SESSION  :
  +             isSSL = true;
  +             req.setAttribute("javax.servlet.request.ssl_session",
  +                               msg.getString());
  +                break;
  +             
  +         case SC_A_REQ_ATTRIBUTE :
  +             req.setAttribute(msg.getString(), 
  +                              msg.getString());
  +                break;
  +
  +         default:
  +             if (decodeMoreHeaders(req, attributeCode, msg) != 500)
  +                 break;
  +
  +             return 500;
  +            }
  +        }
  +
  +        if(isSSL) {
  +            req.setScheme(req.SCHEME_HTTPS);
  +            req.setSecure(true);
  +        }
  +
  +        // set cookies on request now that we have all headers
  +        req.cookies().setHeaders(req.headers());
  +
  +     // Check to see if there should be a body packet coming along
  +     // immediately after
  +     if(req.getContentLength() > 0) {
  +
  +         /* Read present data */
  +         int err = receive(inBuf);
  +            if(err < 0) {
  +             return 500;
  +         }
  +         
  +         blen = inBuf.peekInt();
  +         pos = 0;
  +         inBuf.getBytes(bodyBuff);
  +     }
  +    
  +        if (debug > 5) {
  +            log(req.toString());
  +        }
  +
  +        return 200; // Success
  +    }
       
       /**
        * Handle the Initial Login Message from Web-Server
  @@ -388,7 +694,7 @@
        * 
         * Send Login Seed (MD5 of seed)
         */
  -    private int handleLogInit( Ajp13Packet msg ) throws IOException
  +    private int handleLogInit( Ajp14Packet msg ) throws IOException
       {
        webserverNegociation = msg.getLongInt();
        webserverName            = msg.getString();
  @@ -413,7 +719,7 @@
        * If the authentification is valid send back LogOk
        * If the authentification failed send back LogNok
        */
  -    private int handleLogComp( Ajp13Packet msg ) throws IOException
  +    private int handleLogComp( Ajp14Packet msg ) throws IOException
       {
        // log("in handleLogComp :");
        
  @@ -461,50 +767,356 @@
        return (304);
       }
   
  -    private int handleContextQuery( Ajp13Packet msg ) throws IOException
  +    private int handleContextQuery( Ajp14Packet msg ) throws IOException
       {
        log("in handleContextQuery :");
        return (304);
       }
       
  -    private int handleStatus( Ajp13Packet msg ) throws IOException
  +    private int handleStatus( Ajp14Packet msg ) throws IOException
       {
        log("in handleStatus :");
        return (304);
       }
   
  -    private int handleShutdown( Ajp13Packet msg ) throws IOException
  +    private int handleShutdown( Ajp14Packet msg ) throws IOException
       {
        log("in handleShutdown :");
        return (304);
       }
       
  -    private int handleContextState( Ajp13Packet msg ) throws IOException
  +    private int handleContextState( Ajp14Packet msg ) throws IOException
       {
        log("in handleContextState :");
        return (304);
       }
       
  -    private int handleUnknowPacket( Ajp13Packet msg ) throws IOException
  +    private int handleUnknowPacket( Ajp14Packet msg ) throws IOException
       {
        log("in handleUnknowPacket :");
        return (304);
       }
  +
  +    // ==================== Servlet Input Support =================
  +
  +    public int available() throws IOException {
  +        if (debug > 0) {
  +            log("available()");
  +        }
  +
  +        if (pos >= blen) {
  +            if( ! refillReadBuffer()) {
  +             return 0;
  +         }
  +        }
  +        return blen - pos;
  +    }
   
  -    int decodeMoreHeaders(AjpRequest req, byte attribute, Ajp13Packet msg)
  +    /**
  +     * Return the next byte of request body data (to a servlet).
  +     *
  +     * @see Request#doRead
  +     */
  +    public int doRead() throws IOException 
       {
  -     if (attribute == SC_A_SSL_KEY_SIZE) {
  -         req.setAttribute("javax.servlet.request.key_size", 
Integer.toString(msg.getInt()));
  -         return 200;
  +        if (debug > 0) {
  +            log("doRead()");
  +        }
  +
  +        if(pos >= blen) {
  +            if( ! refillReadBuffer()) {
  +             return -1;
  +         }
  +        }
  +        return (char) bodyBuff[pos++];
  +    }
  +    
  +    /**
  +     * Store a chunk of request data into the passed-in byte buffer.
  +     *
  +     * @param b A buffer to fill with data from the request.
  +     * @param off The offset in the buffer at which to start filling.
  +     * @param len The number of bytes to copy into the buffer.
  +     *
  +     * @return The number of bytes actually copied into the buffer, or -1
  +     * if the end of the stream has been reached.
  +     *
  +     * @see Request#doRead
  +     */
  +    public int doRead(byte[] b, int off, int len) throws IOException 
  +    {
  +        if (debug > 0) {
  +            log("doRead(byte[], int, int)");
  +        }
  +
  +     if(pos >= blen) {
  +         if( ! refillReadBuffer()) {
  +             return -1;
  +         }
        }
  -     return 500; // Error
  +
  +     if(pos + len <= blen) { // Fear the off by one error
  +         // Sanity check b.length > off + len?
  +         System.arraycopy(bodyBuff, pos, b, off, len);
  +         pos += len;
  +         return len;
  +     }
  +
  +     // Not enough data (blen < pos + len)
  +     int toCopy = len;
  +     while(toCopy > 0) {
  +         int bytesRemaining = blen - pos;
  +         if(bytesRemaining < 0) 
  +             bytesRemaining = 0;
  +         int c = bytesRemaining < toCopy ? bytesRemaining : toCopy;
  +
  +         System.arraycopy(bodyBuff, pos, b, off, c);
  +
  +         toCopy    -= c;
  +
  +         off       += c;
  +         pos       += c; // In case we exactly consume the buffer
  +
  +         if(toCopy > 0) 
  +             if( ! refillReadBuffer()) { // Resets blen and pos
  +                 break;
  +             }
  +     }
  +
  +     return len - toCopy;
       }
  +    
  +    /**
  +     * Get more request body data from the web server and store it in the 
  +     * internal buffer.
  +     *
  +     * @return true if there is more data, false if not.    
  +     */
  +    private boolean refillReadBuffer() throws IOException 
  +    {
  +        if (debug > 0) {
  +            log("refillReadBuffer()");
  +        }
  +
  +     // If the server returns an empty packet, assume that that end of
  +     // the stream has been reached (yuck -- fix protocol??).
  +
  +     // Why not use outBuf??
  +     inBuf.reset();
  +     inBuf.appendByte(JK_AJP13_GET_BODY_CHUNK);
  +     inBuf.appendInt(MAX_READ_SIZE);
  +     send(inBuf);
  +     
  +     int err = receive(inBuf);
  +        if(err < 0) {
  +         throw new IOException();
  +     }
  +     
  +     blen = inBuf.peekInt();
  +     pos = 0;
  +     inBuf.getBytes(bodyBuff);
  +
  +     return (blen > 0);
  +    }    
   
  +    // ==================== Servlet Output Support =================
  +    
  +    /**
  +     */
  +    public void beginSendHeaders(int status,
  +                                 String statusMessage,
  +                                 int numHeaders) throws IOException {
  +
  +        if (debug > 0) {
  +            log("sendHeaders()");
  +        }
  +
  +     // XXX if more headers that MAX_SIZE, send 2 packets!
  +
  +     outBuf.reset();
  +        outBuf.appendByte(JK_AJP13_SEND_HEADERS);
  +
  +        if (debug > 0) {
  +            log("status is:  " + status +
  +                       "(" + statusMessage + ")");
  +        }
  +
  +        // set status code and message
  +        outBuf.appendInt(status);
  +        outBuf.appendString(statusMessage);
  +
  +        // write the number of headers...
  +        outBuf.appendInt(numHeaders);
  +    }
  +
  +    public void sendHeader(String name, String value) throws IOException {
  +        int sc = headerNameToSc(name);
  +        if(-1 != sc) {
  +            outBuf.appendInt(sc);
  +        } else {
  +            outBuf.appendString(name);
  +        }
  +        outBuf.appendString(value);
  +    }
  +
  +    public void endSendHeaders() throws IOException {
  +        outBuf.end();
  +        send(outBuf);
  +    }
  +
  +    /**
  +     * Send the HTTP headers back to the web server and on to the browser.
  +     *
  +     * @param status The HTTP status code to send.
  +     * @param headers The set of all headers.
  +     */
  +    public void sendHeaders(int status, MimeHeaders headers)
  +        throws IOException {
  +        sendHeaders(status, HttpMessages.getMessage(status), headers);
  +    }
  +    
  +    /**
  +     * Send the HTTP headers back to the web server and on to the browser.
  +     *
  +     * @param status The HTTP status code to send.
  +     * @param statusMessage the HTTP status message to send.
  +     * @param headers The set of all headers.
  +     */
  +    public void sendHeaders(int status, String statusMessage, MimeHeaders headers)
  +        throws IOException {
  +     // XXX if more headers that MAX_SIZE, send 2 packets!
  +
  +     outBuf.reset();
  +        outBuf.appendByte(JK_AJP13_SEND_HEADERS);
  +        outBuf.appendInt(status);
  +     
  +     outBuf.appendString(statusMessage);
  +        
  +     int numHeaders = headers.size();
  +        outBuf.appendInt(numHeaders);
  +        
  +     for( int i=0 ; i < numHeaders ; i++ ) {
  +         String headerName = headers.getName(i).toString();
  +         int sc = headerNameToSc(headerName);
  +            if(-1 != sc) {
  +                outBuf.appendInt(sc);
  +            } else {
  +                outBuf.appendString(headerName);
  +            }
  +            outBuf.appendString(headers.getValue(i).toString() );
  +        }
  +
  +        outBuf.end();
  +        send(outBuf);
  +    } 
  +
  +
  +    /**
  +     * Translate an HTTP response header name to an integer code if
  +     * possible.  Case is ignored.
  +     * 
  +     * @param name The name of the response header to translate.
  +     *
  +     * @return The code for that header name, or -1 if no code exists.
  +     */
  +    protected int headerNameToSc(String name)
  +    {       
  +        switch(name.charAt(0)) {
  +     case 'c':
  +     case 'C':
  +         if(name.equalsIgnoreCase("Content-Type")) {
  +             return SC_RESP_CONTENT_TYPE;
  +         } else if(name.equalsIgnoreCase("Content-Language")) {
  +             return SC_RESP_CONTENT_LANGUAGE;
  +         } else if(name.equalsIgnoreCase("Content-Length")) {
  +             return SC_RESP_CONTENT_LENGTH;
  +         }
  +            break;
  +            
  +     case 'd':
  +     case 'D':
  +         if(name.equalsIgnoreCase("Date")) {
  +                return SC_RESP_DATE;
  +         }
  +            break;
  +            
  +     case 'l':
  +     case 'L':
  +         if(name.equalsIgnoreCase("Last-Modified")) {
  +             return SC_RESP_LAST_MODIFIED;
  +         } else if(name.equalsIgnoreCase("Location")) {
  +             return SC_RESP_LOCATION;
  +         }
  +            break;
  +
  +     case 's':
  +     case 'S':
  +         if(name.equalsIgnoreCase("Set-Cookie")) {
  +             return SC_RESP_SET_COOKIE;
  +         } else if(name.equalsIgnoreCase("Set-Cookie2")) {
  +             return SC_RESP_SET_COOKIE2;
  +         }
  +            break;
  +            
  +     case 'w':
  +     case 'W':
  +         if(name.equalsIgnoreCase("WWW-Authenticate")) {
  +             return SC_RESP_WWW_AUTHENTICATE;
  +         }
  +            break;          
  +        }
  +        
  +        return -1;
  +    }
  +
  +    
  +    /**
  +     * Signal the web server that the servlet has finished handling this
  +     * request, and that the connection can be reused.
  +     */
  +    public void finish() throws IOException {
  +        if (debug > 0) {
  +            log("finish()");
  +        }
  +
  +     outBuf.reset();
  +        outBuf.appendByte(JK_AJP13_END_RESPONSE);
  +        outBuf.appendBool(true); // Reuse this connection
  +        outBuf.end();
  +        send(outBuf);
  +    }
  +
  +    /**
  +     * Send a chunk of response body data to the web server and on to the
  +     * browser.
  +     *
  +     * @param b A huffer of bytes to send.
  +     * @param off The offset into the buffer from which to start sending.
  +     * @param len The number of bytes to send.
  +     */    
  +    public void doWrite(byte b[], int off, int len) throws IOException {
  +        if (debug > 0) {
  +            log("doWrite(byte[], " + off + ", " + len + ")");
  +        }
  +
  +     int sent = 0;
  +     while(sent < len) {
  +         int to_send = len - sent;
  +         to_send = to_send > MAX_SEND_SIZE ? MAX_SEND_SIZE : to_send;
  +
  +         outBuf.reset();
  +         outBuf.appendByte(JK_AJP13_SEND_BODY_CHUNK);                        
  +         outBuf.appendBytes(b, off + sent, to_send);         
  +         send(outBuf);
  +         sent += to_send;
  +     }
  +    }
  +    
       // ========= Internal Packet-Handling Methods =================
   
       /**
        * Read in a packet from the web server and store it in the passed-in
  -     * <CODE>Ajp13Packet</CODE> object.
  +     * <CODE>Ajp14Packet</CODE> object.
        *
        * @param msg The object into which to store the incoming packet -- any
        * current contents will be overwritten.
  @@ -512,7 +1124,7 @@
        * @return The number of bytes read on a successful read or -1 if there 
        * was an error.
        **/
  -    public int receive(Ajp13Packet msg) throws IOException {
  +    public int receive(Ajp14Packet msg) throws IOException {
        // XXX If the length in the packet header doesn't agree with the
        // actual number of bytes read, it should probably return an error
        // value.  Also, callers of this method never use the length
  @@ -525,9 +1137,10 @@
            if( debug > 5 ) log( "Error reading " + rd );
            return rd; 
        }
  -     if( debug > 5 ) log( "Read " + rd );
        
        int len = msg.checkIn();
  +
  +     if( debug > 5 ) log( "Received " + rd + " " + len + " " + b[0] );
        
        // XXX check if enough space - it's assert()-ed !!!
   
  @@ -542,6 +1155,7 @@
            }
                    total_read += rd;
        }
  +     if( debug > 10 ) log( "Received total:  " + total_read );
        return total_read;
       }
   
  @@ -551,10 +1165,11 @@
        * @param msg A packet with accumulated data to send to the server --
        * this method will write out the length in the header.  
        */
  -    protected void send( Ajp13Packet msg ) throws IOException {
  +    protected void send( Ajp14Packet msg ) throws IOException {
        msg.end(); // Write the packet header
        byte b[] = msg.getBuff();
        int len  = msg.getLen();
  +        if (debug > 5 ) log("send() " + len + " " + b[0] );
        out.write( b, 0, len );
       }
        
  @@ -566,6 +1181,8 @@
        * @see Ajp14Interceptor#processConnection
        */
       public void close() throws IOException {
  +        if (debug > 0) log("close()");
  +
        if(null != out) {        
            out.close();
        }
  
  
  
  1.2       +461 -44   
jakarta-tomcat-connectors/jk/java/org/apache/ajp/Ajp14Packet.java
  
  Index: Ajp14Packet.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-tomcat-connectors/jk/java/org/apache/ajp/Ajp14Packet.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- Ajp14Packet.java  2001/06/24 20:44:49     1.1
  +++ Ajp14Packet.java  2001/06/26 19:52:19     1.2
  @@ -85,56 +85,473 @@
    * @author Henri Gomez [[EMAIL PROTECTED]]
    * @author Dan Milstein [[EMAIL PROTECTED]]
    * @author Keith Wannamaker [[EMAIL PROTECTED]]
  + * @author Kevin Seguin
  + * @author Costin Manolache
    */
  +public class Ajp14Packet {
   
  -public class Ajp14Packet extends Ajp13Packet {
  +    public static final String DEFAULT_CHAR_ENCODING = "8859_1";
  +    public static final int    AJP13_WS_HEADER       = 0x1234;
  +    public static final int    AJP13_SW_HEADER       = 0x4142;  // 'AB'
   
  -     /**
  -      * Create a new packet with an internal buffer of given size.
  -      */
  -     public Ajp14Packet( int size ) {
  -             super(size);
  -     }
  +    /**
  +     * encoding to use when converting byte[] <-> string
  +     */
  +    String encoding = DEFAULT_CHAR_ENCODING;
   
  -     public Ajp14Packet( byte b[] ) {
  -             super(b);
  -     }
  +    /**
  +     * Holds the bytes of the packet
  +     */
  +    byte buff[];
  +
  +    /**
  +     * The current read or write position in the buffer
  +     */
  +    int pos;    
  +
  +    /**
  +     * This actually means different things depending on whether the
  +     * packet is read or write.  For read, it's the length of the
  +     * payload (excluding the header).  For write, it's the length of
  +     * the packet as a whole (counting the header).  Oh, well.
  +     */
  +    int len; 
  +    
  +    /**
  +     * Create a new packet with an internal buffer of given size.
  +     */
  +    public Ajp14Packet( int size ) {
  +        buff = new byte[size];
  +    }
  +    
  +    public Ajp14Packet( byte b[] ) {
  +        buff = b;
  +    }
  +
  +        /**
  +     * Set the encoding to use for byte[] <-> string
  +     * conversions.
  +     * @param encoding the encoding to use.
  +     */
  +    public void setEncoding(String encoding) {
  +        this.encoding = encoding;
  +    }
  +
  +    /**
  +     * Get the encoding used for byte[] <-> string
  +     * conversions.
  +     * @return the encoding used.
  +     */
  +    public String getEncoding() {
  +        return encoding;
  +    }
  +
  +    /**
  +     * Get the internal buffer
  +     * @return internal buffer
  +     */
  +    public byte[] getBuff() {
  +        return buff;
  +    }
  +
  +    /**
  +     * Get length.
  +     * @return length -- This actually means different things depending on whether 
the
  +     *                   packet is read or write.  For read, it's the length of the
  +     *                   payload (excluding the header).  For write, it's the 
length of
  +     *                   the packet as a whole (counting the header).  Oh, well.
  +     */
  +    public int getLen() {
  +        return len;
  +    }
  +
  +    /**
  +     * Get offset into internal buffer.
  +     * @return offset
  +     */
  +    public int getByteOff() {
  +        return pos;
  +    }
  +
  +    /**
  +     * Set offset into internal buffer.
  +     * @param c new offset
  +     */
  +    public void setByteOff(int c) {
  +        pos=c;
  +    }
  +
  +    /**
  +     * For a packet to be sent to the web server, finish the process of
  +     * accumulating data and write the length of the data payload into
  +     * the header.  
  +     */
  +    public void end() {
  +        len = pos;
  +        setInt( 2, len-4 );
  +    }
  +     
  +    // ============ Data Writing Methods ===================
   
  -//   public Ajp14Packet( OutputBuffer ob ) {
  -//           super(ob);
  -//   }
  -     
  -     /** 
  -      * Parse the packet header for a packet sent from the web server to
  -      * the container.  Set the read position to immediately after
  -      * the header.
  -      *
  -      * @return The length of the packet payload, as encoded in the
  -      * header, or -1 if the packet doesn't have a valid header.  
  -      */
  -     public int checkIn() {
  -         pos = 0;
  -         int mark = getInt();
  -         len      = getInt();
  +    /**
  +     * Write an 32 bit integer at an arbitrary position in the packet,but don't
  +     * change the write position.
  +     *
  +     * @param bpos The 0-indexed position within the buffer at which to
  +     * write the integer (where 0 is the beginning of the header).
  +     * @param val The integer to write.
  +     */
  +    private void setInt( int bPos, int val ) {
  +        buff[bPos]   = (byte) ((val >>>  8) & 0xFF);
  +        buff[bPos+1] = (byte) (val & 0xFF);
  +    }
  +
  +    public void appendInt( int val ) {
  +        setInt( pos, val );
  +        pos += 2;
  +    }
  +     
  +    public void appendByte( byte val ) {
  +        buff[pos++] = val;
  +    }
  +     
  +    public void appendBool( boolean val) {
  +        buff[pos++] = (byte) (val ? 1 : 0);
  +    }
  +
  +    /**
  +     * Write a String out at the current write position.  Strings are
  +     * encoded with the length in two bytes first, then the string, and
  +     * then a terminating \0 (which is <B>not</B> included in the
  +     * encoded length).  The terminator is for the convenience of the C
  +     * code, where it saves a round of copying.  A null string is
  +     * encoded as a string with length 0.  
  +     */
  +    public void appendString(String str) throws UnsupportedEncodingException {
  +        // Dual use of the buffer - as Ajp13Packet and as OutputBuffer
  +        // The idea is simple - fewer buffers, smaller footprint and less
  +        // memcpy. The code is a bit tricky, but only local to this
  +        // function.
  +        if(str == null) {
  +            setInt( pos, 0);
  +            buff[pos + 2] = 0;
  +            pos += 3;
  +            return;
  +        }
  +
  +        //
  +        // XXX i don't have OutputBuffer in tc4... ks.
  +        // fix this later...
  +        //
  +        byte[] bytes = str.getBytes(encoding);
  +        appendBytes(bytes, 0, bytes.length);
  +        
  +        /* XXX XXX XXX XXX Try to add it back.
  +        int strStart=pos;
  +
  +        // This replaces the old ( buggy and slow ) str.length()
  +        // and str.getBytes(). str.length() is chars, may be != bytes
  +        // and getBytes is _very_ slow.
  +        // XXX setEncoding !!!
  +
  +        ob.setByteOff( pos+2 ); 
  +        try {
  +            ob.write( str );
  +            ob.flushChars();
  +        } catch( IOException ex ) {
  +            ex.printStackTrace();
  +        }
  +        int strEnd=ob.getByteOff();
  +        
  +        buff[strEnd]=0; // The \0 terminator
  +        int strLen=strEnd-strStart;
  +        setInt( pos, strEnd - strStart );
  +        pos += strLen + 3; 
  +        */
  +    }
  +
  +    /** 
  +     * Copy a chunk of bytes into the packet, starting at the current
  +     * write position.  The chunk of bytes is encoded with the length
  +     * in two bytes first, then the data itself, and finally a
  +     * terminating \0 (which is <B>not</B> included in the encoded
  +     * length).
  +     *
  +     * @param b The array from which to copy bytes.
  +     * @param off The offset into the array at which to start copying
  +     * @param len The number of bytes to copy.  
  +     */
  +    public void appendBytes( byte b[], int off, int numBytes ) {
  +        appendInt( numBytes );
  +        if( pos + numBytes >= buff.length ) {
  +            System.out.println("Buffer overflow " + buff.length + " " + pos + " " + 
numBytes );
  +            // XXX Log
  +        }
  +        System.arraycopy( b, off, buff, pos, numBytes);
  +        buff[pos + numBytes] = 0; // Terminating \0
  +        pos += numBytes + 1;
  +    }
  +
  +        /**
  +     * Write a 32 bits integer at an arbitrary position in the packet, but don't
  +     * change the write position.
  +     *
  +     * @param bpos The 0-indexed position within the buffer at which to
  +     * write the integer (where 0 is the beginning of the header).
  +     * @param val The integer to write.
  +     */
  +    private void setLongInt( int bPos, int val ) {
  +        buff[bPos]   = (byte) ((val >>>  24) & 0xFF);
  +        buff[bPos+1] = (byte) ((val >>>  16) & 0xFF);
  +        buff[bPos+2] = (byte) ((val >>>   8) & 0xFF);
  +        buff[bPos+3] = (byte) (val & 0xFF);
  +    }
  +
  +    public void appendLongInt( int val ) {
  +        setLongInt( pos, val );
  +        pos += 4;
  +    }
  +
  +    /**
  +     * Copy a chunk of bytes into the packet, starting at the current
  +     * write position.  The chunk of bytes IS NOT ENCODED with ANY length
  +     * header.
  +     *
  +     * @param b The array from which to copy bytes.
  +     * @param off The offset into the array at which to start copying
  +     * @param len The number of bytes to copy.
  +     */
  +    public void appendXBytes(byte[] b, int off, int numBytes) {
  +        if( pos + numBytes > buff.length ) {
  +        System.out.println("appendXBytes - Buffer overflow " + buff.length + " " + 
pos + " " + numBytes );
  +        // XXX Log
  +        }
  +        System.arraycopy( b, off, buff, pos, numBytes);
  +        pos += numBytes;
  +    }
  +     
  +    
  +    // ============ Data Reading Methods ===================
  +
  +    /**
  +     * Read an integer from packet, and advance the read position past
  +     * it.  Integers are encoded as two unsigned bytes with the
  +     * high-order byte first, and, as far as I can tell, in
  +     * little-endian order within each byte.  
  +     */
  +    public int getInt() {
  +        int result = peekInt();
  +        pos += 2;
  +        return result;
  +    }
  +
  +    /**
  +     * Read an integer from the packet, but don't advance the read
  +     * position past it.  
  +     */
  +    public int peekInt() {
  +        int b1 = buff[pos] & 0xFF;  // No swap, Java order
  +        int b2 = buff[pos + 1] & 0xFF;
  +
  +        return  (b1<<8) + b2;
  +    }
  +
  +    public byte getByte() {
  +        byte res = buff[pos];
  +        pos++;
  +        return res;
  +    }
  +
  +    public byte peekByte() {
  +        return buff[pos];
  +    }
  +
  +    public boolean getBool() {
  +        return (getByte() == (byte) 1);
  +    }
  +
  +    public void getMessageBytes(MessageBytes mb) {
  +        int length = getInt();
  +        if( (length == 0xFFFF) || (length == -1) ) {
  +            mb.setString( null );
  +            return;
  +        }
  +        mb.setBytes( buff, pos, length );
  +        pos += length;
  +        pos++; // Skip the terminating \0
  +    }
  +    
  +    public MessageBytes addHeader(MimeHeaders headers) {
  +        int length = getInt();
  +        if( (length == 0xFFFF) || (length == -1) ) {
  +            return null;
  +        }
  +        MessageBytes vMB=headers.addValue( buff, pos, length );
  +        pos += length;
  +        pos++; // Skip the terminating \0
            
  -         if( mark != 0x1235 ) {
  -             // XXX Logging
  -             System.out.println("BAD packet " + mark);
  -             dump( "In: " );
  -             return -1;
  -         }
  -         return len;
  -     }
  +        return vMB;
  +    }
  +     
  +    /**
  +     * Read a String from the packet, and advance the read position
  +     * past it.  See appendString for details on string encoding.
  +     **/
  +    public String getString() throws java.io.UnsupportedEncodingException {
  +        int length = getInt();
  +        if( (length == 0xFFFF) || (length == -1) ) {
  +            //           System.out.println("null string " + length);
  +            return null;
  +        }
  +        String s = new String(buff, pos, length, encoding);
  +
  +        pos += length;
  +        pos++; // Skip the terminating \0
  +        return s;
  +    }
  +
  +    /**
  +     * Copy a chunk of bytes from the packet into an array and advance
  +     * the read position past the chunk.  See appendBytes() for details
  +     * on the encoding.
  +     *
  +     * @return The number of bytes copied.
  +     */
  +    public int getBytes(byte dest[]) {
  +        int length = getInt();
  +        if( length > buff.length ) {
  +            // XXX Should be if(pos + length > buff.legth)?
  +            System.out.println("XXX Assert failed, buff too small ");
  +        }
        
  -     /**
  -      * Prepare this packet for accumulating a message from the container to
  -      * the web server.  Set the write position to just after the header
  -      * (but leave the length unwritten, because it is as yet unknown).  
  -      */
  -     public void reset() {
  -         len = 4;
  -         pos = 4;
  -         buff[0] = (byte)0x12;
  -         buff[1] = (byte)0x35;
  +        if( (length == 0xFFFF) || (length == -1) ) {
  +            System.out.println("null string " + length);
  +            return 0;
  +        }
  +
  +        System.arraycopy( buff, pos,  dest, 0, length );
  +        pos += length;
  +        pos++; // Skip terminating \0  XXX I believe this is wrong but harmless
  +        return length;
  +    }
  +
  +        /**
  +     * Read a 32 bits integer from packet, and advance the read position past
  +     * it.  Integers are encoded as four unsigned bytes with the
  +     * high-order byte first, and, as far as I can tell, in
  +     * little-endian order within each byte.
  +     */
  +    public int getLongInt() {
  +        int result = peekLongInt();
  +        pos += 4;
  +        return result;
  +    }
  +
  +    /**
  +     * Copy a chunk of bytes from the packet into an array and advance
  +     * the read position past the chunk.  See appendXBytes() for details
  +     * on the encoding.
  +     *
  +     * @return The number of bytes copied.
  +     */
  +    public int getXBytes(byte[] dest, int length) {
  +        if( length > buff.length ) {
  +        // XXX Should be if(pos + length > buff.legth)?
  +        System.out.println("XXX Assert failed, buff too small ");
  +        }
  +
  +        System.arraycopy( buff, pos,  dest, 0, length );
  +        pos += length;
  +        return length;
  +    }
  +
  +    /**
  +     * Read a 32 bits integer from the packet, but don't advance the read
  +     * position past it.
  +     */
  +    public int peekLongInt() {
  +        int b1 = buff[pos] & 0xFF;  // No swap, Java order
  +        b1 <<= 8;
  +        b1 |= (buff[pos + 1] & 0xFF);
  +        b1 <<= 8;
  +        b1 |= (buff[pos + 2] & 0xFF);
  +        b1 <<=8;
  +        b1 |= (buff[pos + 3] & 0xFF);
  +        return  b1;
  +    }
  +
  +    // ============== Debugging code =========================
  +    private String hex( int x ) {
  +        //       if( x < 0) x=256 + x;
  +        String h=Integer.toHexString( x );
  +        if( h.length() == 1 ) h = "0" + h;
  +        return h.substring( h.length() - 2 );
  +    }
  +
  +    private void hexLine( int start ) {
  +        for( int i=start; i< start+16 ; i++ ) {
  +            if( i < len + 4)
  +                System.out.print( hex( buff[i] ) + " ");
  +            else 
  +                System.out.print( "   " );
  +        }
  +        System.out.print(" | ");
  +        for( int i=start; i < start+16 && i < len + 4; i++ ) {
  +            if( Character.isLetterOrDigit( (char)buff[i] ))
  +                System.out.print( new Character((char)buff[i]) );
  +            else
  +                System.out.print( "." );
  +        }
  +        System.out.println();
  +    }
  +    
  +    public void dump(String msg) {
  +        System.out.println( msg + ": " + buff + " " + pos +"/" + (len + 4));
  +
  +        for( int j=0; j < len + 4; j+=16 )
  +            hexLine( j );
  +     
  +        System.out.println();
  +    }
  +    
  +    /** 
  +     * Parse the packet header for a packet sent from the web server to
  +     * the container.  Set the read position to immediately after
  +     * the header.
  +     *
  +     * @return The length of the packet payload, as encoded in the
  +     * header, or -1 if the packet doesn't have a valid header.  
  +     */
  +    public int checkIn() {
  +     pos = 0;
  +     int mark = getInt();
  +     len      = getInt();
  +
  +     // XXX Insure backward compat !!
  +     //        if( mark != AJP13_WS_HEADER ) {
  +     if( mark != 0x1235 ) {
  +         // XXX Logging
  +         System.out.println("BAD packet " + mark);
  +         dump( "In: " );
  +         return -1;
        }
  +     return len;
  +    }
  +    
  +    /**
  +     * Prepare this packet for accumulating a message from the container to
  +     * the web server.  Set the write position to just after the header
  +     * (but leave the length unwritten, because it is as yet unknown).  
  +     */
  +    public void reset() {
  +     len = 4;
  +     pos = 4;
  +     buff[0] = (byte)0x12;
  +     buff[1] = (byte)0x35;
  +     // XXX Insure backward compat !!
  +     //         buff[0] = (byte)(AJP13_SW_HEADER >> 8);
  +     //         buff[1] = (byte)(AJP13_SW_HEADER & 0xFF);
  +    }
  +     
  +
   }
  
  
  
  1.5       +19 -19    
jakarta-tomcat-connectors/jk/java/org/apache/ajp/tomcat33/Ajp14Interceptor.java
  
  Index: Ajp14Interceptor.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-tomcat-connectors/jk/java/org/apache/ajp/tomcat33/Ajp14Interceptor.java,v
  retrieving revision 1.4
  retrieving revision 1.5
  diff -u -r1.4 -r1.5
  --- Ajp14Interceptor.java     2001/06/24 22:29:02     1.4
  +++ Ajp14Interceptor.java     2001/06/26 19:52:24     1.5
  @@ -163,7 +163,7 @@
        Ajp14 ajp14=new Ajp14();
        ajp14.setContainerSignature( ContextManager.TOMCAT_NAME +
                                     " v" + ContextManager.TOMCAT_VERSION);
  -     AjpRequest ajpreq=new AjpRequest();
  +     BaseRequest ajpreq=new BaseRequest();
        log( "Setting pass " + password );
        ajp14.setPassword( password );
        req=new Ajp14Request(ajp14, ajpreq);
  @@ -186,7 +186,7 @@
               Ajp14Request req=initRequest( thData );
               Ajp14Response res= (Ajp14Response)req.getResponse();
               Ajp14 ajp14=req.ajp14;
  -         AjpRequest ajpReq=req.ajpReq;
  +         BaseRequest ajpReq=req.ajpReq;
   
               ajp14.setSocket(socket);
   
  @@ -279,18 +279,18 @@
   class Ajp14Request extends Request 
   {
       Ajp14 ajp14;
  -    AjpRequest ajpReq;
  +    BaseRequest ajpReq;
       
  -    public Ajp14Request(Ajp14 ajp14, AjpRequest ajpReq) 
  +    public Ajp14Request(Ajp14 ajp14, BaseRequest ajpReq) 
       {
  -     headers = ajpReq.getHeaders();
  -     methodMB=ajpReq.getMethod();
  -     protoMB=ajpReq.getProtocol();
  -     uriMB = ajpReq.getRequestURI();
  -     queryMB = ajpReq.getQueryString();
  -     remoteAddrMB = ajpReq.getRemoteAddr();
  -     remoteHostMB = ajpReq.getRemoteHost();
  -     serverNameMB = ajpReq.getServerName();
  +     headers = ajpReq.headers();
  +     methodMB=ajpReq.method();
  +     protoMB=ajpReq.protocol();
  +     uriMB = ajpReq.requestURI();
  +     queryMB = ajpReq.queryString();
  +     remoteAddrMB = ajpReq.remoteAddr();
  +     remoteHostMB = ajpReq.remoteHost();
  +     serverNameMB = ajpReq.serverName();
   
        // XXX sync cookies 
        scookies = new Cookies( headers );
  @@ -308,7 +308,7 @@
       }
   
       // -------------------- Wrappers for changed method names, and to use the 
buffers
  -    // XXX Move AjpRequest into util !!! ( it's just a stuct with some MessageBytes 
)
  +    // XXX Move BaseRequest into util !!! ( it's just a stuct with some 
MessageBytes )
   
       public int getServerPort() {
           return ajpReq.getServerPort();
  @@ -320,30 +320,30 @@
   
       public  void setRemoteUser( String s ) {
        super.setRemoteUser(s);
  -     ajpReq.getRemoteUser().setString(s);
  +     ajpReq.remoteUser().setString(s);
       }
   
       public String getRemoteUser() {
  -     String s=ajpReq.getRemoteUser().toString();
  +     String s=ajpReq.remoteUser().toString();
        if( s == null )
            s=super.getRemoteUser();
        return s;
       }
   
       public String getAuthType() {
  -     return ajpReq.getAuthType().toString();
  +     return ajpReq.authType().toString();
       }
       
       public void setAuthType(String s ) {
  -     ajpReq.getAuthType().setString(s);
  +     ajpReq.authType().setString(s);
       }
   
       public String getJvmRoute() {
  -     return ajpReq.getJvmRoute().toString();
  +     return ajpReq.jvmRoute().toString();
       }
       
       public void setJvmRoute(String s ) {
  -     ajpReq.getJvmRoute().setString(s);
  +     ajpReq.jvmRoute().setString(s);
       }
   
       // XXX scheme
  
  
  

Reply via email to