You are viewing a plain text version of this content. The canonical link for it is here.
Posted to soap-dev@ws.apache.org by wc...@locus.apache.org on 2000/11/28 23:37:01 UTC
cvs commit: xml-soap/java/src/org/apache/soap/transport TransportMessage.java
wcloeten 00/11/28 14:37:00
Added: java/src/org/apache/soap/transport TransportMessage.java
Log:
semi-transport-independent encapsulation of message content
Revision Changes Path
1.1 xml-soap/java/src/org/apache/soap/transport/TransportMessage.java
Index: TransportMessage.java
===================================================================
/*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 2000 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "SOAP" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation and was
* originally based on software copyright (c) 2000, International
* Business Machines, Inc., http://www.apache.org. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
package org.apache.soap.transport;
import java.io.*;
import java.util.*;
import javax.activation.*;
import javax.mail.*;
import javax.mail.internet.*;
import org.w3c.dom.*;
import org.apache.soap.*;
import org.apache.soap.encoding.*;
import org.apache.soap.rpc.*;
import org.apache.soap.util.*;
import org.apache.soap.util.xml.*;
import org.apache.soap.util.mime.*;
import org.apache.soap.transport.EnvelopeEditor;
/**
* Transport type-independent encapsulation of SOAP message content.
*
* @author Wouter Cloetens
*/
public class TransportMessage
{
private String contentType = null;
private int offset = 0;
private byte[] bytes = null;
private String envelope = null;
private Hashtable headers = null;
private SOAPContext ctx = null;
/**
* Create a message from an already built envelope.
* Call save() to generate the byte array.
*/
public TransportMessage(String envelope, SOAPContext ctx,
Hashtable headers)
throws IllegalArgumentException, MessagingException,
IOException, SOAPException {
this.envelope = envelope;
this.ctx = ctx;
if (headers != null)
this.headers = headers;
else
this.headers = new Hashtable();
}
/**
* Create 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.
*/
public TransportMessage(InputStream is, int contentLength,
String contentType, SOAPContext ctx)
throws IllegalArgumentException, MessagingException,
IOException, SOAPException {
headers = new Hashtable();
this.ctx = ctx;
this.contentType = contentType;
if (contentLength < 0)
throw new SOAPException (Constants.FAULT_CODE_PROTOCOL,
"Content length must be specified.");
bytes = new byte[contentLength];
int offset = 0;
int bytesRead = 0;
// We're done reading when we get all the content OR when the stream
// returns a -1.
while ((offset < contentLength) && (bytesRead >= 0)) {
bytesRead = is.read(bytes, offset, contentLength - offset);
offset += bytesRead;
}
if (offset < contentLength)
throw new SOAPException (Constants.FAULT_CODE_PROTOCOL,
"Premature end of stream. Data is truncated. Read "
+ offset + " bytes successfully, expected "
+ contentLength);
}
/**
* Apply envelope editor on inbound message.
*/
public void editIncoming(EnvelopeEditor editor)
throws SOAPException, IOException {
editEnvelope(editor, true);
}
/**
* Apply envelope editor on outgoing message.
*/
public void editOutgoing(EnvelopeEditor editor)
throws SOAPException, IOException {
editEnvelope(editor, false);
}
/*
* Transport-Hook-Extension
*/
protected void editEnvelope(EnvelopeEditor editor, boolean isIncoming)
throws SOAPException, IOException {
String opt = System.getProperties().getProperty(
Constants.TRANSPORT_HOOK_EXTENSION);
if (opt != null && editor != null) {
StringWriter tout = new StringWriter();
if (isIncoming)
editor.editIncoming(getEnvelopeReader(), tout);
else
editor.editOutgoing(getEnvelopeReader(), tout);
tout.flush();
envelope = tout.toString();
}
}
/**
* Interpret byte array and extract SOAPContext and SOAP envelope (as
* a String). Make sure content type is set first.
*/
public String read()
throws IllegalArgumentException, MessagingException,
IOException, SOAPException {
// Parse and validate content type.
ContentType cType = null;
try {
cType = new ContentType(contentType);
} catch(ParseException pe) {
}
if (cType == null
|| !(cType.match (Constants.HEADERVAL_CONTENT_TYPE)
|| cType.match (Constants.HEADERVAL_CONTENT_TYPE_MULTIPART)))
throw new SOAPException(Constants.FAULT_CODE_PROTOCOL,
"Unsupported content type \"" +
contentType + "\", must be: \"" +
Constants.HEADERVAL_CONTENT_TYPE + "\" or \"" +
Constants.HEADERVAL_CONTENT_TYPE_MULTIPART + '"');
byte[] envBytes;
String charset;
if (cType.match(Constants.HEADERVAL_CONTENT_TYPE_MULTIPART)) {
// Parse multipart request.
ByteArrayDataSource ds = new ByteArrayDataSource(bytes,
contentType);
// Parse multipart mime into parts.
ctx.readMultipart(ds);
// Find root part.
BodyPart soapPart = ctx.getRootPart();
ContentType ctype = new ContentType(
soapPart.getContentType());
if (!(ctype.match(Constants.HEADERVAL_CONTENT_TYPE))) {
throw new SOAPException(Constants.FAULT_CODE_PROTOCOL,
"Content type of first MIME part must be: '" +
Constants.HEADERVAL_CONTENT_TYPE + "'.");
}
// Extract SOAP envelope.
charset = ctype.getParameter("charset");
ByteArrayDataSource bads = new ByteArrayDataSource(
(InputStream)soapPart.getContent(), null);
envBytes = bads.toByteArray();
} else {
// Parse plain request.
charset = cType.getParameter("charset");
envBytes = bytes;
}
// Hmm, risky, the default charset is transport-specific...
if (charset == null || charset.equals(""))
charset = Constants.HEADERVAL_DEFAULT_CHARSET;
envelope = new String(envBytes, MimeUtility.javaCharset(charset));
return envelope;
}
/**
* Parse envelope.
*/
public Envelope unmarshall(XMLParserLiaison xpl)
throws SOAPException {
Document doc = xpl.read("SOAP Envelope", getEnvelopeReader());
if (doc == null) {
throw new SOAPException(Constants.FAULT_CODE_CLIENT,
"parsing error");
}
return Envelope.unmarshall(doc.getDocumentElement());
}
/**
* Write message to byte array. Override this method for
* transport types that encode in a non-standard way.
*/
public void save()
throws IllegalArgumentException, MessagingException, IOException {
byte[] envBytes = envelope.getBytes("UTF8");
if (ctx.getCount() == 0) {
// Non-multipart.
contentType = Constants.HEADERVAL_CONTENT_TYPE_UTF8;
bytes = envBytes;
}
else {
// Add the SOAP response part.
ByteArrayDataSource ds = new ByteArrayDataSource(envBytes,
Constants.HEADERVAL_CONTENT_TYPE_UTF8);
DataHandler dh = new DataHandler(ds);
MimeBodyPart bp = new MimeBodyPart();
bp.setDataHandler(dh);
bp.setHeader(Constants.HEADER_CONTENT_LENGTH,
String.valueOf(ds.getSize()));
// Since we're going to send UTF-8 data, avoid letting JavaMail
// determine a transfer-encoding of quoted-printable or base64.
bp.setHeader("Content-Transfer-Encoding", "8bit");
String start = ctx.setRootPart(bp);
// Print the whole response to a byte array.
ByteArrayOutputStream multipartPayload =
new ByteArrayOutputStream();
ctx.writeTo (multipartPayload);
bytes = multipartPayload.toByteArray();
// Now strip off the headers. (Grmbl, get rid of JavaMail
// for MIME support).
StringBuffer namebuf = new StringBuffer();
StringBuffer valuebuf = new StringBuffer();
boolean parsingName = true;
for (offset = 0; offset < bytes.length; offset++) {
if (bytes[offset] == '\n') {
if (namebuf.length() == 0)
break;
String name = namebuf.toString();
if (name.equals(Constants.HEADER_CONTENT_TYPE))
contentType = valuebuf.toString()
+ "; type=\""
+ Constants.HEADERVAL_CONTENT_TYPE
+ "\"; " + start;
else
setHeader(name, valuebuf.toString());
namebuf = new StringBuffer();
valuebuf = new StringBuffer();
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]);
}
}
}
}
/**
* Get SOAP Envelope as a String.
*/
public String getEnvelope() {
return envelope;
}
/**
* Get SOAP Envelope as a Reader.
*/
public Reader getEnvelopeReader() {
return new StringReader(envelope);
}
/**
* Set SOAP Envelope.
*/
public void setEnvelope(String envelope) {
this.envelope = envelope;
}
/**
* Get Content-Type.
*/
public String getContentType() {
return contentType;
}
/**
* Set Content-Type as String.
*/
public void setContentType(String contentType) {
this.contentType = contentType;
}
/**
* Get size of response content in bytes.
*/
public int getContentLength() {
return bytes.length - offset;
}
/**
* Set a response header.
*/
public void setHeader(String name, String value) {
headers.put(name, value);
}
/**
* Get a response header.
*/
public String getHeader(String name) {
return (String)headers.get(name);
}
/**
* Get response header names.
*/
public Enumeration getHeaderNames() {
return headers.keys();
}
/**
* Get response header hashtable.
*/
public Hashtable getHeaders() {
return headers;
}
/**
* Write content.
*/
public void writeTo(OutputStream outStream) throws IOException {
outStream.write(bytes, offset, bytes.length - offset);
outStream.flush();
}
/**
* Set the byte array of the response.
*/
public void setBytes(byte[] data) {
offset = 0;
bytes = data;
}
/**
* Set the byte array of the response.
*/
public void readFully(InputStream is) throws IOException {
offset = 0;
ByteArrayDataSource bads = new ByteArrayDataSource(is, null);
bytes = bads.toByteArray();
}
/**
* Get the response byte array.
*/
public byte[] getBytes() {
// The offset trick is an efficiency hack. Eliminate that here.
if(offset != 0) {
byte[] data = new byte[bytes.length - offset];
System.arraycopy(bytes, offset, data, 0, data.length);
bytes = data;
offset = 0;
}
return bytes;
}
}