Return-Path: Mailing-List: contact soap-dev-help@xml.apache.org; run by ezmlm Delivered-To: mailing list soap-dev@xml.apache.org Delivered-To: moderator for soap-dev@xml.apache.org Received: (qmail 78200 invoked by uid 500); 14 Nov 2002 05:00:46 -0000 Delivered-To: apmail-xml-soap-cvs@apache.org Received: (qmail 78197 invoked from network); 14 Nov 2002 05:00:46 -0000 Received: from icarus.apache.org (63.251.56.143) by daedalus.apache.org with SMTP; 14 Nov 2002 05:00:46 -0000 Received: (qmail 94403 invoked by uid 1527); 14 Nov 2002 05:00:43 -0000 Date: 14 Nov 2002 05:00:43 -0000 Message-ID: <20021114050043.94400.qmail@icarus.apache.org> From: snichol@apache.org To: xml-soap-cvs@apache.org Subject: cvs commit: xml-soap/java/src/org/apache/soap Constants.java X-Spam-Rating: daedalus.apache.org 1.6.2 0/1000/N 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. + * + * + * @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. + * + * + * @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 =