snichol     2002/11/13 21:00:42

  Modified:    java/src/org/apache/soap/transport TransportMessage.java
               java/src/org/apache/soap Constants.java
  Log:
  Fix a bug introduced in previous changes when their is an envelope editor
  and no envelope was explitictly specified (just a rootPart in SOAPContext).
  Prevent duplicate assignment of rootPart when original envelope is empty.
  Side-step MIME serialization when the message is only a SOAP envelope.
  Improve MIME serialization by starting with a larger byte output stream.
  Move some constants.
  
  Revision  Changes    Path
  1.19      +207 -112  
xml-soap/java/src/org/apache/soap/transport/TransportMessage.java
  
  Index: TransportMessage.java
  ===================================================================
  RCS file: 
/home/cvs/xml-soap/java/src/org/apache/soap/transport/TransportMessage.java,v
  retrieving revision 1.18
  retrieving revision 1.19
  diff -u -r1.18 -r1.19
  --- TransportMessage.java     12 Nov 2002 14:34:56 -0000      1.18
  +++ TransportMessage.java     14 Nov 2002 05:00:42 -0000      1.19
  @@ -89,6 +89,19 @@
       protected String envelope = null;
       protected Hashtable headers = null;
       protected SOAPContext ctx = null;
  +    /**
  +     * Tracks whether the SOAPContext rootPart is the same as the Envelope.
  +     * Used for outgoing messages to avoid multiple assignment of the rootPart.
  +     */
  +    protected boolean rootPartIsEnvelope;
  +    /**
  +     * The envelope in DOM form (for incoming messages).
  +     */
  +    protected Document envelopeDocument = null;
  +    /**
  +     * The envelope in SOAP form (for incoming messages).
  +     */
  +    protected Envelope soapEnvelope = null;
   
       /**
        * No-argument constructor.
  @@ -97,13 +110,22 @@
       }
   
       /**
  -     * Create a message from an already built envelope and/or
  +     * Creates a message from an already built envelope and/or
        * SOAPContext. The envelope argument may be null.
  -     * Call save() to generate the byte array.
  +     * <ul>
  +     * <li>Call editOutgoing() to filter/transform the string SOAP envelope.</li>
  +     * <li>Call save() to generate the byte array.</li>
  +     * </ul>
  +     *
  +     * @param envelope The SOAP envelope.  Can be null.
  +     * @param ctx The SOAP context.  Can contain the SOAP envelope if the
  +     *            envelope parameter is null.
  +     * @param headers The transport headers.
        */
       public TransportMessage(String envelope, SOAPContext ctx,
                               Hashtable headers) {
           this.envelope = envelope;
  +        rootPartIsEnvelope = false;
           this.ctx = ctx;
           if (headers != null)
               this.headers = headers;
  @@ -112,10 +134,25 @@
       }
   
       /**
  -     * Create a message from an InputStream. This reads the InputStream and
  +     * Creates a message from an InputStream. This reads the InputStream and
        * stores it in a byte array.
  -     * Call read() to extract the SOAPContext and SOAP
  -     * envelope from the byte array.
  +     * <ul>
  +     * <li>Call read() to extract the SOAPContext and SOAP
  +     * envelope (as a String) from the byte array.</li>
  +     * <li>Call editIncoming() to filter/transform the string SOAP envelope.</li>
  +     * <li>Call unmarshall() to extract the DOM and SOAP envelope
  +     * from the string SOAP envelope.</li>
  +     * </ul>
  +     *
  +     * @param is The stream from which to read.  Use a buffered stream if that
  +     *           would help performance.
  +     * @param contentLength The number of bytes to read.  Negative values
  +     *                      mean read to end of stream.
  +     * @param contentType The content type (MIME format, e.g. text/xml).
  +     * @param ctx The SOAP context.
  +     * @param headers The transport headers, e.g. HTTP headers.
  +     * @exception IOException For errors reading the stream.
  +     * @exception SOAPException If fewer than contentLength bytes can be read.
        */
       public TransportMessage(InputStream is, int contentLength,
                               String contentType, SOAPContext ctx,
  @@ -197,15 +234,22 @@
                   editor.editOutgoing(getEnvelopeReader(), tout);
               tout.flush();
               envelope = tout.toString();
  +            rootPartIsEnvelope = false;
           }
       }
   
       /**
  -     * Interpret byte array and extract SOAPContext and SOAP envelope (as
  -     * a String). Make sure content type is set before calling this.
  +     * Extracts the SOAPContext and SOAP envelope (as a String) from the
  +     * byte array read in the constructor.
  +     * Make sure content type is set before calling this.
        * Note that in the Messaging scenario, the root type is not necessarily
  -     * a SOAP envelope, or even text. If it is text, the text is returned and
  -     * it is up to the invoker to check the root part's Content-Type
  +     * a SOAP envelope, or even text.
  +     *
  +     * @return If there is a SOAP envelope or the root part is text, the text.
  +     *         It is up to the invoker to check the root part's Content-Type.
  +     * @exception MessagingException For MIME errors.
  +     * @exception IOException For errors reading byte streams.
  +     * @exception SOAPException For errors such as missing content type.
        */
       public String read()
           throws MessagingException,
  @@ -230,14 +274,14 @@
   
           // Check encoding
           String encoding = (String) HTTPUtils.getHeaderValue(headers,
  -                                                            "Accept-Encoding");
  +                                                            
Constants.HEADER_ACCEPT_ENCODING);
           if (encoding != null)
  -            ctx.setAcceptGzip(encoding.indexOf("gzip") != -1);
  +            
ctx.setAcceptGzip(encoding.indexOf(Constants.HEADERVAL_CONTENT_ENCODING) != -1);
   
           encoding = (String) HTTPUtils.getHeaderValue(headers,
  -                                                     "Content-Encoding");
  +                                                     
Constants.HEADER_CONTENT_ENCODING);
           boolean gzip = false;
  -        if (encoding != null && encoding.indexOf("gzip") != -1)
  +        if (encoding != null && 
encoding.indexOf(Constants.HEADERVAL_CONTENT_ENCODING) != -1)
               gzip = true;
           ctx.setGzip(gzip);
           if (gzip) {
  @@ -308,28 +352,36 @@
       }
   
       /**
  -     * Parse envelope.
  +     * Parses the String envelope into a DOM and SOAP envelope.
  +     *
  +     * @param xdb A DOM document builder.
  +     * @return A SOAP envelope.
  +     * @exception SOAPException For parsing errors or empty documents.
        */
       public Envelope unmarshall(DocumentBuilder xdb)
           throws SOAPException {
  -        Document doc;
  +        // TODO: compare to parsing in Call; both should be done in one piece of 
code.
           try {
  -            doc = xdb.parse(new InputSource(getEnvelopeReader()));
  +            envelopeDocument = xdb.parse(new InputSource(getEnvelopeReader()));
           } catch(Exception e) {
               throw new SOAPException(Constants.FAULT_CODE_CLIENT,
  -                                    "parsing error: " + e);
  +                                    "parsing error: " + e, e);
           }
  -        if (doc == null) {
  +        if (envelopeDocument == null) {
               throw new SOAPException(Constants.FAULT_CODE_CLIENT,
                                       "parsing error: received empty document");
           }
   
  -        return Envelope.unmarshall(doc.getDocumentElement());
  +        soapEnvelope = Envelope.unmarshall(envelopeDocument.getDocumentElement());
  +        return soapEnvelope;
       }
   
       /**
  -     * Write message to byte array. Override this method for
  +     * Writes the message to byte array. Override this method for
        * transport types that encode in a non-standard way.
  +     *
  +     * @exception MessagingException For MIME errors.
  +     * @exception IOException For errors reading byte streams.
        */
       public void save()
           throws MessagingException, IOException {
  @@ -351,93 +403,85 @@
           }
           if (rootContentType == null)
               rootContentType = Constants.HEADERVAL_CONTENT_TYPE_UTF8;
  -        if (getEnvelope() != null) {
  +        if (getEnvelope() != null && !rootPartIsEnvelope) {
               ctx.setRootPart(envelope, rootContentType);
  -        } else {
  -            MimeBodyPart rootPart = ctx.getRootPart();
  -            if (rootPart != null) {
  -                String ctype = rootPart.getContentType();
  -                ContentType type = null;
  -                try {
  -                    type = new ContentType(ctype);
  -                }
  -                catch (ParseException e) {}
  -
  -                if (type != null && Constants.CTYPE_TEXT_ALL.match(type)) {
  -                    ByteArrayDataSource ds = new ByteArrayDataSource(
  -                        rootPart.getInputStream(), ctype);
  -                     envelope = ds.getText();
  -                }
  -            }
  +            rootPartIsEnvelope = true;
           }
   
  -        // Print the whole response to a byte array.
  -        ByteArrayOutputStream payload =
  -            new ByteArrayOutputStream(1024);
  -        ctx.writeTo(payload);
  -        bytes = payload.toByteArray();
  -
  -        // Now strip off the headers. (Grmbl, get rid of JavaMail
  -        // for MIME support). Just intercept the Content-Type
  -        // header. We don't want any of the MIME headers, and we know the
  -        // overall Content-Length anyway.
  -        StringBuffer namebuf = new StringBuffer(64);
  -        StringBuffer valuebuf = new StringBuffer(64);
  -        boolean parsingName = true;
  -        for (offset = 0; offset < bytes.length; offset++) {
  -            if (bytes[offset] == '\n') {
  -                // JavaMail sometimes word-wraps header parameters to the
  -                // next line. Throttle through that.
  -                if ((offset + 1 < bytes.length) &&
  -                    ((bytes[offset + 1] == ' ') ||
  -                     (bytes[offset + 1] == '\t'))) {
  -                    while (((++offset) + 1 < bytes.length) &&
  -                           ((bytes[offset + 1] == ' ')
  -                           || (bytes[offset + 1] == '\t')))
  -                      ;
  -                    continue;
  -                }
  -                if (namebuf.length() == 0) {
  -                    offset++;
  -                    break;
  -                }
  -                String name = namebuf.toString();
  -                // For multipart, append type and start parameters to
  -                // Content-Type header.
  -                if (name.equals(Constants.HEADER_CONTENT_TYPE)) {
  -                    contentType = valuebuf.toString();
  -                    if (ctx.getCount() > 1) {
  -                        String rootCID = ctx.getRootPart().getContentID();
  -                        // Strip < and > off Content-ID.
  -                        rootCID = rootCID.substring(1, rootCID.length() - 1);
  -                        contentType += "; type=\""
  -                            + Constants.HEADERVAL_CONTENT_TYPE
  -                            + "\"; start=\"" + rootCID + '"';
  +        // If we only have one part, it is the envelope, so handle
  +        // that case more directly, i.e. without JavaMail.
  +        if (ctx.getCount() == 1) {
  +            bytes = envelope.getBytes(MimeUtils.getEncoding(rootContentType, 
"UTF8"));
  +            offset = 0;
  +            contentType = rootContentType;
  +        } else {
  +            // Print the whole response to a byte array.
  +            ByteArrayOutputStream payload =
  +                new ByteArrayOutputStream(65536);
  +            ctx.writeTo(payload);
  +            bytes = payload.toByteArray();
  +    
  +            // Now strip off the headers. (Grmbl, get rid of JavaMail
  +            // for MIME support). Just intercept the Content-Type
  +            // header. We don't want any of the MIME headers, and we know the
  +            // overall Content-Length anyway.
  +            StringBuffer namebuf = new StringBuffer(64);
  +            StringBuffer valuebuf = new StringBuffer(64);
  +            boolean parsingName = true;
  +            for (offset = 0; offset < bytes.length; offset++) {
  +                if (bytes[offset] == '\n') {
  +                    // JavaMail sometimes word-wraps header parameters to the
  +                    // next line. Throttle through that.
  +                    if ((offset + 1 < bytes.length) &&
  +                        ((bytes[offset + 1] == ' ') ||
  +                         (bytes[offset + 1] == '\t'))) {
  +                        while (((++offset) + 1 < bytes.length) &&
  +                               ((bytes[offset + 1] == ' ')
  +                               || (bytes[offset + 1] == '\t')))
  +                          ;
  +                        continue;
                       }
  -                }
  -                namebuf = new StringBuffer(64);
  -                valuebuf = new StringBuffer(64);
  -                parsingName = true;
  -                }
  -            else if (bytes[offset] != '\r') {
  -                if (parsingName) {
  -                    if (bytes[offset] == ':') {
  -                        parsingName = false;
  +                    if (namebuf.length() == 0) {
                           offset++;
  +                        break;
                       }
  -                    else
  -                        namebuf.append((char)bytes[offset]);
  -                }
  -                else
  +                    String name = namebuf.toString();
  +                    // For multipart, append type and start parameters to
  +                    // Content-Type header.
  +                    if (name.equals(Constants.HEADER_CONTENT_TYPE)) {
  +                        contentType = valuebuf.toString();
  +                        if (ctx.getCount() > 1) {
  +                            String rootCID = ctx.getRootPart().getContentID();
  +                            // Strip < and > off Content-ID.
  +                            rootCID = rootCID.substring(1, rootCID.length() - 1);
  +                            contentType += "; type=\""
  +                                + Constants.HEADERVAL_CONTENT_TYPE
  +                                + "\"; start=\"" + rootCID + '"';
  +                        }
  +                    }
  +                    namebuf = new StringBuffer(64);
  +                    valuebuf = new StringBuffer(64);
  +                    parsingName = true;
  +                } else if (bytes[offset] != '\r') {
  +                    if (parsingName) {
  +                        if (bytes[offset] == ':') {
  +                            parsingName = false;
  +                            offset++;
  +                        } else {
  +                            namebuf.append((char)bytes[offset]);
  +                        }
  +                    } else {
                           valuebuf.append((char)bytes[offset]);
  +                    }
  +                }
               }
           }
  -        // TODO: should not send for HTTP response
  -        headers.put("Accept-Encoding", "x-gzip");
  +        // TODO: need not send for HTTP response
  +        headers.put(Constants.HEADER_ACCEPT_ENCODING, 
Constants.HEADERVAL_ACCEPT_ENCODING);
           if (Boolean.TRUE.equals(ctx.getGzip())) {
               // Deflate
               ByteArrayOutputStream baos =
  -                               new ByteArrayOutputStream(bytes.length * 2);
  +                               new ByteArrayOutputStream(bytes.length);
               GZIPOutputStream gzos = new GZIPOutputStream(baos);
               gzos.write(bytes, offset, bytes.length - offset);
               gzos.close();
  @@ -445,27 +489,50 @@
               bytes = baos.toByteArray();
               offset = 0;
   
  -            headers.put("Content-Encoding", "x-gzip");
  +            headers.put(Constants.HEADER_CONTENT_ENCODING, 
Constants.HEADERVAL_CONTENT_ENCODING);
           }
       }
   
       /**
  -     * Get SOAPContext.
  +     * Gets the SOAPContext.
  +     *
  +     * @return The SOAP context.
        */
       public SOAPContext getSOAPContext() {
           return ctx;
       }
   
       /**
  -     * Get SOAP Envelope as a String.
  +     * Gets the SOAP Envelope as a String.
  +     *
  +     * @return The SOAP envelope as a string.
        */
       public String getEnvelope() {
  +        if (envelope == null) {
  +            // Assign the root part, if any, to the envelope.
  +            try {
  +                MimeBodyPart rootPart = ctx.getRootPart();
  +                if (rootPart != null) {
  +                    String ctype = rootPart.getContentType();
  +                    ContentType type = new ContentType(ctype);
  +                    if (type != null && Constants.CTYPE_TEXT_ALL.match(type)) {
  +                        ByteArrayDataSource ds = new ByteArrayDataSource(
  +                            rootPart.getInputStream(), ctype);
  +                        envelope = ds.getText();
  +                    }
  +                    rootPartIsEnvelope = true;
  +                }
  +            } catch (Exception e) {
  +            }
  +        }
           return envelope;
       }
   
       /**
  -     * Get SOAP Envelope/root part as a Reader. Returns null if the root part
  +     * Gets SOAP Envelope/root part as a Reader. Returns null if the root part
        * is not text.
  +     *
  +     * @return A reader for the SOAP envelope.
        */
       public Reader getEnvelopeReader() {
           if (getEnvelope() == null)
  @@ -475,63 +542,86 @@
       }
   
       /**
  -     * Set SOAP Envelope.
  +     * Sets the SOAP Envelope.
  +     *
  +     * @param envelope The SOAP envelope as a string.
        */
       public void setEnvelope(String envelope) {
           this.envelope = envelope;
  +        rootPartIsEnvelope = false;
       }
   
       /**
  -     * Get Content-Type.
  +     * Gets the Content-Type.
  +     *
  +     * @return The content type (MIME).
        */
       public String getContentType() {
           return contentType;
       }
   
       /**
  -     * Set Content-Type as String.
  +     * Sets the Content-Type as String.
  +     *
  +     * @param contentType The content type (MIME).
        */
       public void setContentType(String contentType) {
           this.contentType = contentType;
       }
   
       /**
  -     * Get size of response content in bytes.
  +     * Gets the size of response content in bytes.
  +     *
  +     * @return The size of the content in bytes.
        */
       public int getContentLength() {
           return bytes.length - offset;
       }
   
       /**
  -     * Set a transport header.
  +     * Sets a transport header.  If a header with the same name already
  +     * exists, it is overwritten.
  +     *
  +     * @param name The header name.
  +     * @param value The header value.
        */
       public void setHeader(String name, String value) {
           headers.put(name, value);
       }
   
       /**
  -     * Get a transport header.
  +     * Gets a transport header.
  +     *
  +     * @param name The header name.
  +     * @return The header value.
        */
       public String getHeader(String name) {
           return (String)headers.get(name);
       }
   
       /**
  -     * Get transport header names.
  +     * Gets transport header names.
  +     *
  +     * @return The transport header names.
        */
       public Enumeration getHeaderNames() {
           return headers.keys();
       }
   
       /**
  -     * Get the complete header hashtable.
  +     * Gets the complete header hashtable.
  +     *
  +     * @return The transport headers in a Hashtable.
        */
       public Hashtable getHeaders() {
           return headers;
       }
   
       /**
  -     * Write content.
  +     * Writes the content to a stream.
  +     *
  +     * @param outStream The stream to which to write.
  +     * @exception IOException If an error occurs when writing.
        */
       public void writeTo(OutputStream outStream) throws IOException {
           outStream.write(bytes, offset, bytes.length - offset);
  @@ -539,7 +629,9 @@
       }
   
       /**
  -     * Set the byte array of the response.
  +     * Sets the byte array of the message.
  +     *
  +     * @param data The bytes of the response.
        */
       public void setBytes(byte[] data) {
           offset = 0;
  @@ -547,9 +639,10 @@
       }
   
       /**
  -     * Set the byte array of the response.
  +     * Sets the byte array of the message.
        *
  -     * @deprecated After 2.3.1
  +     * @param is The input stream.
  +     * @exception IOException If an error occurs reading the stream.
        */
       public void readFully(InputStream is) throws IOException {
           offset = 0;
  @@ -558,7 +651,9 @@
       }
   
       /**
  -     * Get the response byte array.
  +     * Gets the message byte array.
  +     *
  +     * @return The message byte array.
        */
       public byte[] getBytes() {
           // The offset trick is an efficiency hack. Eliminate that here.
  
  
  
  1.28      +4 -0      xml-soap/java/src/org/apache/soap/Constants.java
  
  Index: Constants.java
  ===================================================================
  RCS file: /home/cvs/xml-soap/java/src/org/apache/soap/Constants.java,v
  retrieving revision 1.27
  retrieving revision 1.28
  diff -u -r1.27 -r1.28
  --- Constants.java    12 Nov 2002 14:34:56 -0000      1.27
  +++ Constants.java    14 Nov 2002 05:00:42 -0000      1.28
  @@ -124,6 +124,8 @@
     public static final String HEADER_AUTHORIZATION = "Authorization";
     public static final String HEADER_PROXY_AUTHORIZATION =
       "Proxy-Authorization";
  +  public static final String HEADER_ACCEPT_ENCODING = "Accept-Encoding";
  +  public static final String HEADER_CONTENT_ENCODING = "Content-Encoding";
   
     // HTTP header field values.
     public static final String HEADERVAL_DEFAULT_CHARSET = "iso-8859-1";
  @@ -137,6 +139,8 @@
     public static final String HEADERVAL_CONTENT_TYPE_MULTIPART =
       HEADERVAL_CONTENT_TYPE_MULTIPART_PRIMARY + '/' +
       HEADERVAL_MULTIPART_CONTENT_SUBTYPE;
  +  public static final String HEADERVAL_ACCEPT_ENCODING = "gzip";
  +  public static final String HEADERVAL_CONTENT_ENCODING = "gzip";
   
     // XML Declaration string
     public static final String XML_DECL = 
  
  
  

--
To unsubscribe, e-mail:   <mailto:soap-dev-unsubscribe@;xml.apache.org>
For additional commands, e-mail: <mailto:soap-dev-help@;xml.apache.org>

Reply via email to