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;
      }
  }