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>