rineholt 2002/06/30 12:59:04 Modified: java/src/org/apache/axis Message.java MessageContext.java java/src/org/apache/axis/attachments Attachments.java AttachmentsImpl.java MultiPartRelatedInputStream.java java/src/org/apache/axis/client Call.java java/src/org/apache/axis/transport/http AxisServlet.java java/src/org/apache/axis/utils JavaUtils.java axisNLS.properties Added: java/src/org/apache/axis/attachments DimeBodyPart.java DimeDelimitedInputStream.java DimeMultiPart.java DimeTypeNameFormat.java MultiPartDimeInputStream.java MultiPartInputStream.java Log: Support for DIME according to the new released spec at: http://msdn.microsoft.com/library/en-us/dnglobspec/html/draft-nielsen-dime-02.txt Revision Changes Path 1.76 +2 -3 xml-axis/java/src/org/apache/axis/Message.java Index: Message.java =================================================================== RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/Message.java,v retrieving revision 1.75 retrieving revision 1.76 diff -u -r1.75 -r1.76 --- Message.java 29 Jun 2002 16:39:51 -0000 1.75 +++ Message.java 30 Jun 2002 19:59:04 -0000 1.76 @@ -96,7 +96,6 @@ // MIME parts defined for messages. public static final String MIME_MULTIPART_RELATED = "multipart/related"; - // NOT SUPPORTED NOW public static final String MIME_APPLICATION_DIME = "application/dime"; /** Default Attachments Implementation class */ @@ -385,10 +384,10 @@ } //This will have to give way someday to HTTP Chunking but for now kludge. - public int getContentLength() throws org.apache.axis.AxisFault { + public long getContentLength() throws org.apache.axis.AxisFault { //Force serialization if it hasn't happend it. //Rick Rineholt fix this later. - int ret = mSOAPPart.getAsBytes().length; + long ret = mSOAPPart.getAsBytes().length; if (mAttachments != null && 0 < mAttachments.getAttachmentCount()) { ret = mAttachments.getContentLength(); } 1.106 +17 -2 xml-axis/java/src/org/apache/axis/MessageContext.java Index: MessageContext.java =================================================================== RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/MessageContext.java,v retrieving revision 1.105 retrieving revision 1.106 diff -u -r1.105 -r1.106 --- MessageContext.java 30 Jun 2002 18:15:46 -0000 1.105 +++ MessageContext.java 30 Jun 2002 19:59:04 -0000 1.106 @@ -55,6 +55,7 @@ package org.apache.axis ; +import org.apache.axis.attachments.Attachments; import org.apache.axis.client.AxisClient; import org.apache.axis.description.OperationDesc; import org.apache.axis.description.ServiceDesc; @@ -437,8 +438,22 @@ */ public void setResponseMessage(Message respMsg) { responseMessage = respMsg ; - if (responseMessage != null) responseMessage.setMessageContext(this); - }; + if (responseMessage != null){ + responseMessage.setMessageContext(this); + + //if we have received attachments of a particular type + // than that should be the default type to send. + Message reqMsg= getRequestMessage(); + if( null != reqMsg){ + Attachments reqAttch= reqMsg.getAttachmentsImpl(); + Attachments respAttch= respMsg.getAttachmentsImpl(); + if(null != reqAttch && null != respAttch){ + if(respAttch.getSendType() == Attachments.SEND_TYPE_NOTSET) + respAttch.setSendType(reqAttch.getSendType());//only if not explicity set. + } + } + } + } /** * Return the current (i.e. request before the pivot, response after) 1.9 +35 -1 xml-axis/java/src/org/apache/axis/attachments/Attachments.java Index: Attachments.java =================================================================== RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/attachments/Attachments.java,v retrieving revision 1.8 retrieving revision 1.9 diff -u -r1.8 -r1.9 --- Attachments.java 24 Jun 2002 12:34:51 -0000 1.8 +++ Attachments.java 30 Jun 2002 19:59:04 -0000 1.9 @@ -194,7 +194,7 @@ * * @throws org.apache.axis.AxisFault */ - public int getContentLength() throws org.apache.axis.AxisFault; + public long getContentLength() throws org.apache.axis.AxisFault; /** * Write the content to the stream. @@ -231,4 +231,38 @@ * @return True if value should be treated as an attchment. */ public boolean isAttachment(Object value); + + + /** Use the default attatchment send type. */ + public final int SEND_TYPE_NOTSET = 1; + + /** Use the SOAP with MIME attatchment send type. */ + public final int SEND_TYPE_MIME = 2; //use mime + + /** Use the DIME attatchment type. */ + public final int SEND_TYPE_DIME= 3; //use dime; + + final int SEND_TYPE_MAX = 3; + + /** The default attatchment type. MIME */ + final int SEND_TYPE_DEFAULT = SEND_TYPE_MIME; + + /** + * Set the format for attachments. + * + * @param the format to send. + * SEND_TYPE_MIME for Multipart Releated Mail type attachments. + * SEND_TYPE_DIME for DIME type attachments. + */ + + public void setSendType( int sendtype); + + /** + * Determine if an object is to be treated as an attchment. + * + * + * @return SEND_TYPE_MIME, SEND_TYPE_DIME, SEND_TYPE_NOTSET + */ + + public int getSendType(); } 1.19 +102 -21 xml-axis/java/src/org/apache/axis/attachments/AttachmentsImpl.java Index: AttachmentsImpl.java =================================================================== RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/attachments/AttachmentsImpl.java,v retrieving revision 1.18 retrieving revision 1.19 diff -u -r1.18 -r1.19 --- AttachmentsImpl.java 24 Jun 2002 13:19:10 -0000 1.18 +++ AttachmentsImpl.java 30 Jun 2002 19:59:04 -0000 1.19 @@ -90,8 +90,12 @@ /** * The actual stream to manage the multi-related input stream. */ - protected org.apache.axis.attachments.MultiPartRelatedInputStream mpartStream = + protected MultiPartInputStream mpartStream = null; + /** + * The form of the attachments, whether MIME or DIME. + */ + protected int sendtype= Attachments.SEND_TYPE_NOTSET; /** * This is the content location as specified in SOAP with Attachments. @@ -132,7 +136,7 @@ // Process the input stream for headers to determine the mime // type. - // TODO + //TODO -- This is for transports that do not process headers. } else { java.util.StringTokenizer st = new java.util.StringTokenizer(contentType, " \t;"); @@ -142,6 +146,7 @@ if (mimetype.equalsIgnoreCase( org.apache.axis.Message.MIME_MULTIPART_RELATED)) { + sendtype= SEND_TYPE_MIME; mpartStream = new org.apache.axis.attachments.MultiPartRelatedInputStream( contentType, @@ -165,10 +170,13 @@ soapPart = new org.apache.axis.SOAPPart(null, mpartStream, false); - } else if (mimetype.equalsIgnoreCase( - org.apache.axis.Message.MIME_APPLICATION_DIME)) { // do nothing today. - - // is= new DIMEInputStreamManager(is); + } else if (mimetype.equalsIgnoreCase(org.apache.axis.Message.MIME_APPLICATION_DIME)) { + try{ + mpartStream= + new MultiPartDimeInputStream( (java.io.InputStream) intialContents); + soapPart = new org.apache.axis.SOAPPart(null, mpartStream, false); + }catch(Exception e){ throw org.apache.axis.AxisFault.makeFault(e);} + sendtype= SEND_TYPE_DIME; } } } @@ -206,6 +214,10 @@ public Part removeAttachmentPart(String reference) throws org.apache.axis.AxisFault { + multipart = null; + + dimemultipart = null; + mergeinAttachments(); Part removedPart = getAttachmentByReference(reference); @@ -232,6 +244,10 @@ public Part addAttachmentPart(Part newPart) throws org.apache.axis.AxisFault { + multipart = null; + + dimemultipart = null; + mergeinAttachments(); Part oldPart = (Part) attachments.put(newPart.getContentId(), newPart); @@ -262,6 +278,10 @@ public Part createAttachmentPart(Object datahandler) throws org.apache.axis.AxisFault { + multipart = null; + + dimemultipart = null; + mergeinAttachments(); if (!(datahandler instanceof javax.activation.DataHandler)) { @@ -289,6 +309,8 @@ public void setAttachmentParts(java.util.Collection parts) throws org.apache.axis.AxisFault { + multipart = null; + dimemultipart = null; mergeinAttachments(); attachments.clear(); orderedAttachments.clear(); @@ -395,13 +417,16 @@ try { this.soapPart = (SOAPPart) newRoot; + multipart = null; + dimemultipart = null; } catch (ClassCastException e) { throw new ClassCastException(JavaUtils.getMessage("onlySOAPParts")); } } /** Field multipart */ - public javax.mail.internet.MimeMultipart multipart = null; + javax.mail.internet.MimeMultipart multipart = null; + DimeMultiPart dimemultipart = null; /** * Get the content length of the stream. @@ -410,22 +435,55 @@ * * @throws org.apache.axis.AxisFault */ - public int getContentLength() throws org.apache.axis.AxisFault { + public long getContentLength() throws org.apache.axis.AxisFault { mergeinAttachments(); + int sendtype= this.sendtype == SEND_TYPE_NOTSET ? SEND_TYPE_DEFAULT : this.sendtype; + try { - return (int) org.apache.axis.attachments.MimeUtils.getContentLength( - (multipart != null) - ? multipart - : (multipart = org.apache.axis.attachments.MimeUtils.createMP( - soapPart.getAsString(), orderedAttachments))); + if(sendtype == SEND_TYPE_MIME) + return (int)org.apache.axis.attachments.MimeUtils.getContentLength( + multipart != null ? multipart : (multipart = org.apache.axis.attachments.MimeUtils.createMP(soapPart.getAsString(), orderedAttachments))); + else if (sendtype == SEND_TYPE_DIME)return createDimeMessage().getTransmissionSize(); } catch (Exception e) { throw AxisFault.makeFault(e); } + return 0; } /** + * Creates the DIME message + * + * @return + * + * @throws org.apache.axis.AxisFault + */ + protected DimeMultiPart createDimeMessage() throws org.apache.axis.AxisFault{ + int sendtype= this.sendtype == SEND_TYPE_NOTSET ? SEND_TYPE_DEFAULT : this.sendtype; + if (sendtype == SEND_TYPE_DIME){ + if(dimemultipart== null){ + + dimemultipart= new DimeMultiPart(); + dimemultipart.addBodyPart(new DimeBodyPart( + soapPart.getAsBytes(), DimeTypeNameFormat.URI, + "http://schemas.xmlsoap.org/soap/envelope/", + "uuid:714C6C40-4531-442E-A498-3AC614200295")); + + for( java.util.Iterator i= orderedAttachments.iterator(); + i.hasNext(); ){ + AttachmentPart part= (AttachmentPart)i.next(); + DataHandler dh= AttachmentUtils. + getActivationDataHandler(part); + dimemultipart.addBodyPart(new + DimeBodyPart(dh,part.getContentId())); + } + } + } + return dimemultipart; + } + + /** * Write the content to the stream. * * @param os @@ -434,12 +492,17 @@ */ public void writeContentToStream(java.io.OutputStream os) throws org.apache.axis.AxisFault { + int sendtype= this.sendtype == SEND_TYPE_NOTSET ? + SEND_TYPE_DEFAULT : this.sendtype; + try{ mergeinAttachments(); + if(sendtype == SEND_TYPE_MIME){ org.apache.axis.attachments.MimeUtils.writeToMultiPartStream(os, (multipart != null) ? multipart - : (multipart = org.apache.axis.attachments.MimeUtils.createMP( + : (multipart = + org.apache.axis.attachments.MimeUtils.createMP( soapPart.getAsString(), orderedAttachments))); for (java.util.Iterator i = orderedAttachments.iterator(); @@ -453,6 +516,8 @@ ((ManagedMemoryDataSource) ds).delete(); } } + }else if (sendtype == SEND_TYPE_DIME)createDimeMessage().write(os); + }catch(Exception e){ throw org.apache.axis.AxisFault.makeFault(e);} } /** @@ -466,13 +531,17 @@ mergeinAttachments(); - return org.apache.axis.attachments.MimeUtils.getContentType((multipart - != null) - ? multipart - : (multipart = - org.apache.axis.attachments.MimeUtils.createMP( - soapPart.getAsString(), - orderedAttachments))); + int sendtype= this.sendtype == SEND_TYPE_NOTSET ? SEND_TYPE_DEFAULT : + this.sendtype; + if(sendtype == SEND_TYPE_MIME) + return org.apache.axis.attachments.MimeUtils.getContentType((multipart + != null) + ? multipart + : (multipart = + org.apache.axis.attachments.MimeUtils.createMP( + soapPart.getAsString(), + orderedAttachments))); + else return org.apache.axis.Message.MIME_APPLICATION_DIME; } /** @@ -564,5 +633,17 @@ */ public Part createAttachmentPart() throws org.apache.axis.AxisFault { return new AttachmentPart(); + } + + public void setSendType( int sendtype){ + if( sendtype < 1) + throw new IllegalArgumentException(""); + if( sendtype > SEND_TYPE_MAX ) + throw new IllegalArgumentException(""); + this.sendtype= sendtype; + } + + public int getSendType(){ + return sendtype; } } 1.15 +1 -1 xml-axis/java/src/org/apache/axis/attachments/MultiPartRelatedInputStream.java Index: MultiPartRelatedInputStream.java =================================================================== RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/attachments/MultiPartRelatedInputStream.java,v retrieving revision 1.14 retrieving revision 1.15 diff -u -r1.14 -r1.15 --- MultiPartRelatedInputStream.java 29 Jun 2002 21:55:39 -0000 1.14 +++ MultiPartRelatedInputStream.java 30 Jun 2002 19:59:04 -0000 1.15 @@ -68,7 +68,7 @@ * * @author Rick Rineholt */ -public class MultiPartRelatedInputStream extends java.io.FilterInputStream { +public class MultiPartRelatedInputStream extends MultiPartInputStream{ /** Field log */ protected static Log log = 1.1 xml-axis/java/src/org/apache/axis/attachments/DimeBodyPart.java Index: DimeBodyPart.java =================================================================== /* * The Apache Software License, Version 1.1 * * * Copyright (c) 2001 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 "Axis" 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 [EMAIL PROTECTED] * * 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. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. */ /** * @author Rick Rineholt */ package org.apache.axis.attachments; import java.io.IOException; import java.util.StringTokenizer; import javax.activation.*; import org.apache.axis.transport.http.HTTPConstants; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.axis.utils.JavaUtils; /** * This class is a single part for DIME mulitpart message. DIME 1.0 format 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | VERSION |B|E|C| TYPE_T| OPT_T | OPTIONS_LENGTH | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ID_LENGTH | TYPE_LENGTH | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | DATA_LENGTH | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | / / OPTIONS + PADDING / / | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | / / ID + PADDING / / | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | / / TYPE + PADDING / / | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | / / DATA + PADDING / / | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ /** * Holds one attachment DIME part. */ public class DimeBodyPart { protected static Log log = LogFactory.getLog(DimeBodyPart.class.getName()); protected Object data = null; protected DimeTypeNameFormat dtnf = null; protected byte[] type = null; protected byte[] id = null; final static byte POSITION_FIRST = (byte) 0x04; final static byte POSITION_LAST = (byte) 0x02; private final static byte CHUNK = 0x01; //Means set the chunk bit private final static byte CHUNK_NEXT = 0x2; //Means this was part of a CHUNK private static int MAX_TYPE_LENGTH = (1 << 16) - 1; private static int MAX_ID_LENGTH = (1 << 16) - 1; static final long MAX_DWORD = 0xffffffffL; protected DimeBodyPart() {}; //do not use. /** * Create a DIME Attachment Part. * @param data a byte array containing the data as the attachment. * @param format the type format for the data. * @param type the type of the data * @param id the ID for the DIME part. * */ public DimeBodyPart(byte[] data, DimeTypeNameFormat format, String type, String id) { System.arraycopy(data, 0, this.data = new byte[ data.length], 0, data.length); this.dtnf = format; this.type = type.getBytes(); if (this.type.length > MAX_TYPE_LENGTH) throw new IllegalArgumentException(JavaUtils.getMessage ("attach.dimetypeexceedsmax", "" + this.type.length, "" + MAX_TYPE_LENGTH)); this.id = id.getBytes(); if (this.id.length > MAX_ID_LENGTH) throw new IllegalArgumentException( JavaUtils.getMessage("attach.dimelengthexceedsmax", "" + this.id.length, "" + MAX_ID_LENGTH)); } /** * Create a DIME Attachment Part. * @param dh the data for the attachment as a JAF datahadler. * @param format the type format for the data. * @param type the type of the data * @param id the ID for the DIME part. * */ public DimeBodyPart(DataHandler dh, DimeTypeNameFormat format, String type, String id) { this.data = dh; this.dtnf = format; if (type == null || type.length() == 0) type = "application/octet-stream"; this.type = type.getBytes(); if (this.type.length > MAX_TYPE_LENGTH) throw new IllegalArgumentException(JavaUtils.getMessage( "attach.dimetypeexceedsmax", "" + this.type.length, "" + MAX_TYPE_LENGTH)); this.id = id.getBytes(); if (this.id.length > MAX_ID_LENGTH) throw new IllegalArgumentException(JavaUtils.getMessage( "attach.dimelengthexceedsmax", "" + this.id.length, "" + MAX_ID_LENGTH)); } /** * Create a DIME Attachment Part. * @param dh the data for the attachment as a JAF datahadler. * The type and foramt is derived from the DataHandler. * @param id the ID for the DIME part. * */ public DimeBodyPart(DataHandler dh, String id) { this(dh, DimeTypeNameFormat.MIME, dh.getContentType(), id); String ct = dh.getContentType(); if (ct != null) { ct = ct.trim(); if (ct.toLowerCase().startsWith("application/uri")) { StringTokenizer st = new StringTokenizer(ct, " \t;"); String t = st.nextToken(" \t;"); if (t.equalsIgnoreCase("application/uri")) { for (; st.hasMoreTokens();) { t = st.nextToken(" \t;"); if (t.equalsIgnoreCase("uri")) { t = st.nextToken("="); if (t != null) { t = t.trim(); if (t.startsWith("\"")) t = t.substring(1); if (t.endsWith("\"")) t = t.substring(0, t.length() - 1); this.type = t.getBytes(); this.dtnf = DimeTypeNameFormat.URI; } return; } else if (t.equalsIgnoreCase("uri=")) { t = st.nextToken(" \t;"); if (null != t && t.length() != 0) { t = t.trim(); if (t.startsWith("\"")) t= t.substring(1); if (t.endsWith("\"")) t= t.substring(0, t.length() - 1); this.type = t.getBytes(); this.dtnf = DimeTypeNameFormat.URI; return; } } else if (t.toLowerCase().startsWith("uri=")) { if (-1 != t.indexOf('=')) { t = t.substring(t.indexOf('=')).trim(); if (t.length() != 0) { t = t.trim(); if (t.startsWith("\"")) t = t.substring(1); if (t.endsWith("\"")) t = t.substring(0, t.length() - 1); this.type = t.getBytes(); this.dtnf = DimeTypeNameFormat.URI; return; } } } } } } } } /** * Write to stream the data using maxchunk for the largest junk. * */ void write(java.io.OutputStream os, byte position, long maxchunk) throws java.io.IOException { if (maxchunk < 1) throw new IllegalArgumentException( JavaUtils.getMessage("attach.dimeMaxChunkSize0", "" + maxchunk)); if (maxchunk > MAX_DWORD) throw new IllegalArgumentException( JavaUtils.getMessage("attach.dimeMaxChunkSize1", "" + maxchunk)); if (data instanceof byte[]) send(os, position, (byte[]) data, maxchunk); if (data instanceof DataHandler) send(os, position, (DataHandler) data, maxchunk); } /** * Write to stream the data. * */ void write(java.io.OutputStream os, byte position) throws java.io.IOException { write(os, position, MAX_DWORD); } private static final byte[] pad = new byte[4]; void send(java.io.OutputStream os, byte position, byte[] data, final long maxchunk)throws java.io.IOException { send(os, position, data, 0, data.length, maxchunk); } void send(java.io.OutputStream os, byte position, byte[] data, int offset, final int length, final long maxchunk) throws java.io.IOException { byte chunknext = 0; do { int sendlength = (int) Math.min(maxchunk, length - offset); sendChunk(os, position, data, offset, sendlength, (byte) ((sendlength < (length - offset) ? CHUNK : 0) | chunknext)); offset += sendlength; chunknext = CHUNK_NEXT; } while (offset < length); } void send(java.io.OutputStream os, byte position, DataHandler dh, final long maxchunk) throws java.io.IOException { byte chunknext = 0; long dataSize = getDataSize(); java.io.InputStream in = dh.getInputStream(); byte[] readbuf = new byte[64 * 1024]; int bytesread; sendHeader(os, position, dataSize, (byte) 0); long totalsent = 0; do { bytesread = in.read(readbuf); if (bytesread > 0) { os.write(readbuf, 0, bytesread); totalsent += bytesread; } } while (bytesread > -1); os.write(pad, 0, dimePadding(totalsent)); } protected void sendChunk(java.io.OutputStream os, final byte position, byte[] data, byte chunk) throws java.io.IOException { sendChunk(os, position, data, 0, data.length, chunk); } protected void sendChunk(java.io.OutputStream os, final byte position, byte[] data, int offset, int length, byte chunk) throws java.io.IOException { sendHeader(os, position, length, chunk); os.write(data, offset, length); os.write(pad, 0, dimePadding(length)); } final static byte CURRENT_OPT_T = (byte) 0; protected void sendHeader(java.io.OutputStream os, final byte position, long length, byte chunk) throws java.io.IOException { byte[] fixedHeader = new byte[12]; //VERSION fixedHeader[0] = (DimeMultiPart.CURRENT_VERSION << 3) & 0xf8; // B, E, and C fixedHeader[0] |= (byte) ((position & (byte) 0x6) & ((chunk & CHUNK) != 0 ? ~POSITION_LAST : ~0) & ((chunk & CHUNK_NEXT) != 0 ? ~POSITION_FIRST : ~0)); fixedHeader[0] |= (chunk & CHUNK); //TYPE_T if ((chunk & CHUNK_NEXT) == 0) //If this is a follow on chunk dont send id again. fixedHeader[1] = (byte) ((dtnf.toByte() << 4) & 0xf0); //OPT_T fixedHeader[1] |= (byte) (CURRENT_OPT_T & 0xf); //OPTION_LENGTH fixedHeader[2] = (byte) 0; fixedHeader[3] = (byte) 0; //ID_LENGTH if ((chunk & CHUNK_NEXT) == 0) { //If this is a follow on chunk dont send id again. fixedHeader[4] = (byte) ((id.length >>> 8) & 0xff); fixedHeader[5] = (byte) ((id.length) & 0xff); } //TYPE_LENGTH if ((chunk & CHUNK_NEXT) == 0) { fixedHeader[6] = (byte) ((type.length >>> 8) & 0xff); fixedHeader[7] = (byte) ((type.length) & 0xff); } //DATA_LENGTH fixedHeader[8] = (byte) ((length >>> 24) & 0xff); fixedHeader[9] = (byte) ((length >>> 16) & 0xff); fixedHeader[10] = (byte) ((length >>> 8) & 0xff); fixedHeader[11] = (byte) (length & 0xff); os.write(fixedHeader); //OPTIONS + PADDING // (NONE) //ID + PADDING if ((chunk & CHUNK_NEXT) == 0) { os.write(id); os.write(pad, 0, dimePadding(id.length)); } //TYPE + PADDING if ((chunk & CHUNK_NEXT) == 0) { os.write(type); os.write(pad, 0, dimePadding(type.length)); } } final static int dimePadding(long l) { return (int) ((4L - (l & 0x3L)) & 0x03L); } long getTransmissionSize(long chunkSize) { long size = 0; size += id.length; size += dimePadding(id.length); size += type.length; size += dimePadding(type.length); //no options. long dataSize = getDataSize(); long fullChunks = dataSize / chunkSize; long lastChunkSize = dataSize % chunkSize; if (0 != lastChunkSize) size += 12; //12 bytes for fixed header size += 12 * fullChunks; //add additional header size for each chunk. size += fullChunks * dimePadding(chunkSize); size += dimePadding(lastChunkSize); size += dataSize; return size; } long getTransmissionSize() { return getTransmissionSize(MAX_DWORD); } protected long getDataSize() { if (data instanceof byte[]) return ((byte[]) (data)).length; if (data instanceof DataHandler) return getDataSize((DataHandler) data); return -1; } protected long getDataSize(DataHandler dh) { long dataSize = -1L; try { DataSource ds = dh.getDataSource(); //Do files our selfs since this is costly to read in. Ask the file system. // This is 90% of the use of attachments. if (ds instanceof javax.activation.FileDataSource) { javax.activation.FileDataSource fdh = (javax.activation.FileDataSource) ds; java.io.File df = fdh.getFile(); if (!df.exists()) { throw new RuntimeException( JavaUtils.getMessage("noFile", df.getAbsolutePath())); } dataSize = df.length(); } else { dataSize = 0; java.io.InputStream in = ds.getInputStream(); byte[] readbuf = new byte[64 * 1024]; int bytesread; do { bytesread = in.read(readbuf); if (bytesread > 0) dataSize += bytesread; } while (bytesread > -1); in.close(); } } catch (Exception e) { log.error(JavaUtils.getMessage("exception00"), e); } return dataSize; } } 1.1 xml-axis/java/src/org/apache/axis/attachments/DimeDelimitedInputStream.java Index: DimeDelimitedInputStream.java =================================================================== /* * The Apache Software License, Version 1.1 * * * Copyright (c) 2001 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 "Axis" 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 [EMAIL PROTECTED] * * 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. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. */ package org.apache.axis.attachments; import java.io.IOException; import org.apache.axis.utils.JavaUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.axis.AxisFault; import org.apache.axis.utils.JavaUtils; /** * @author Rick Rineholt */ /** * This class takes the input stream and turns it multiple streams. DIME version 0 format 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ --- | VERSION |B|E|C| TYPE_T| OPT_T | OPTIONS_LENGTH | A +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ID_LENGTH | TYPE_LENGTH | Always present 12 bytes +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ even on chunked data. | DATA_LENGTH | V +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ --- | / / OPTIONS + PADDING / / (absent for version 0) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | / / ID + PADDING / / | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | / / TYPE + PADDING / / | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | / / DATA + PADDING / / | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ public class DimeDelimitedInputStream extends java.io.FilterInputStream { protected static Log log = LogFactory.getLog(DimeDelimitedInputStream.class.getName()); java.io.InputStream is = null; //The source input stream. boolean closed = true; //The stream has been closed. boolean theEnd = false; //There are no more streams left. boolean moreChunks = false; //More chunks are a coming! boolean MB = false; //First part of the stream. MUST be SOAP. boolean ME = false; //Last part of stream. DimeTypeNameFormat tnf = null; String type = null; String id = null; long recordLength = 0L; //length of the record. long bytesRead = 0; //How many bytes of the record have been read. int dataPadLength = 0; //How many pad bytes there are. private static byte[] trash = new byte[4]; protected int streamNo = 0; protected IOException streamInError = null; protected static int streamCount = 0; //number of streams produced. protected synchronized static int newStreamNo() { log.debug(JavaUtils.getMessage("streamNo", "" + (streamCount + 1))); return ++streamCount; } static boolean isDebugEnabled = false; /** * Gets the next stream. From the previous using new buffer reading size. * @return the dime delmited stream. Null if there are no more streams. */ synchronized DimeDelimitedInputStream getNextStream() throws IOException { if (null != streamInError) throw streamInError; if (theEnd) return null; if (bytesRead < recordLength || moreChunks) //Stream must be read in succession throw new RuntimeException(JavaUtils.getMessage( "attach.dimeReadFullyError")); dataPadLength -= readPad(dataPadLength); //Create an new dime stream that comes after this one. return new DimeDelimitedInputStream(this.is); } /** * Create a new dime stream; */ DimeDelimitedInputStream(java.io.InputStream is) throws IOException { super(null); //we handle everything so this is not necessary, don't won't to hang on to a reference. isDebugEnabled = log.isDebugEnabled(); streamNo = newStreamNo(); closed = false; this.is = is; readHeader(false); } private final int readPad(final int size) throws IOException { if (0 == size) return 0; int read = readFromStream(trash, 0, size); if (size != read) { streamInError = new IOException(JavaUtils.getMessage( "attach.dimeNotPaddedCorrectly")); throw streamInError; } return read; } private final int readFromStream(final byte[] b) throws IOException { return readFromStream(b, 0, b.length); } private final int readFromStream(final byte[] b, final int start, final int length) throws IOException { int br = 0; int brTotal = 0; do { try { br = is.read(b, brTotal + start, length - brTotal); } catch (IOException e) { streamInError = e; throw e; }; if (br > 0) brTotal += br; } while (br > -1 && brTotal < length); return brTotal != 0 ? brTotal : br; } /** * Get the id for this stream part. * @return the id; */ public String getContentId() { return id; } /** * Read from the boundary delimited stream. * @param b is the array to read into. * @param off is the offset * @return the number of bytes read. -1 if endof stream. */ public DimeTypeNameFormat getDimeTypeNameFormat() { return tnf; } /** * get type. * @param b is the array to read into. * @param off is the offset * @return the number of bytes read. -1 if endof stream. */ public String getType() { return type; } /** * Read from the boundary delimited stream. * @param b is the array to read into. * @param off is the offset * @return the number of bytes read. -1 if endof stream. */ public synchronized int read(byte[] b, final int off, final int len) throws IOException { if (closed) { dataPadLength -= readPad(dataPadLength); throw new IOException(JavaUtils.getMessage("streamClosed")); } return _read(b, off, len); } protected int _read(byte[] b, final int off, final int len) throws IOException { if (len < 0) throw new IllegalArgumentException (JavaUtils.getMessage("attach.readLengthError", "" + len)); if (off < 0) throw new IllegalArgumentException (JavaUtils.getMessage("attach.readOffsetError", "" + off)); if (b == null) throw new IllegalArgumentException (JavaUtils.getMessage("attach.readArrayNullError")); if (b.length < off + len) throw new IllegalArgumentException (JavaUtils.getMessage("attach.readArraySizeError", "" + b.length, "" + len, "" + off)); if (null != streamInError) throw streamInError; if (0 == len) return 0; //quick. if (bytesRead >= recordLength && !moreChunks) { dataPadLength -= readPad(dataPadLength); return -1; } int totalbytesread = 0; int bytes2read = 0; do { if (bytesRead >= recordLength && moreChunks) readHeader(true); bytes2read = (int) Math.min(recordLength - bytesRead, (long) len - totalbytesread); bytes2read = (int) Math.min(recordLength - bytesRead, (long) len - totalbytesread); try { bytes2read = is.read(b, off + totalbytesread, bytes2read); } catch (IOException e) { streamInError = e; throw e; } if (0 < bytes2read) { totalbytesread += bytes2read; bytesRead += bytes2read; } } while (bytes2read > -1 && totalbytesread < len && (bytesRead < recordLength || moreChunks)); if (0 > bytes2read) { if (moreChunks) { streamInError = new IOException(JavaUtils.getMessage( "attach.DimeStreamError0")); throw streamInError; } if (bytesRead < recordLength) { streamInError = new IOException(JavaUtils.getMessage ("attach.DimeStreamError1", "" + (recordLength - bytesRead))); throw streamInError; } if (!ME) { streamInError = new IOException(JavaUtils.getMessage( "attach.DimeStreamError0")); throw streamInError; } //in theory the last chunk of data should also have been padded, but lets be tolerant of that. dataPadLength = 0; } else if (bytesRead >= recordLength) { //get rid of pading. try { dataPadLength -= readPad(dataPadLength); } catch (IOException e) { //in theory the last chunk of data should also have been padded, but lets be tolerant of that. if (!ME) throw e; else { dataPadLength = 0; streamInError = null; } } } if (bytesRead >= recordLength && ME) { theEnd = true; } return totalbytesread >= 0 ? totalbytesread : -1; } void readHeader(boolean isChunk) throws IOException { bytesRead = 0; //How many bytes of the record have been read. if (isChunk) { if (!moreChunks) throw new RuntimeException( JavaUtils.getMessage("attach.DimeStreamError2")); dataPadLength -= readPad(dataPadLength); //Just incase it was left over. } byte[] header = new byte[12]; if (header.length != readFromStream(header)) { streamInError = new IOException(JavaUtils.getMessage( "attach.DimeStreamError3", "" + header.length)); throw streamInError; } //VERSION byte version = (byte) ((header[0] >>> 3) & 0x1f); if (version > DimeMultiPart.CURRENT_VERSION) { streamInError = new IOException(JavaUtils.getMessage("attach.DimeStreamError4", "" + version, "" + DimeMultiPart.CURRENT_VERSION)); throw streamInError; } //B, E, C MB = 0 != (0x4 & header[0]); ME = 0 != (0x2 & header[0]); moreChunks = 0 != (0x1 & header[0]); //TYPE_T if (!isChunk) tnf = DimeTypeNameFormat.parseByte((byte) ((header[1] >>> 4) & (byte) 0xf)); //OPTIONS_LENGTH int optionsLength = ((((int) header[2]) << 8) & 0xff00) | ((int) header[3]); //ID_LENGTH int idLength = ((((int) header[4]) << 8) & 0xff00) | ((int) header[5]); //TYPE_LENGTH int typeLength = ((((int) header[6]) << 8) & 0xff00) | ((int) header[7]); //DATA_LENGTH recordLength = ((((long) header[8]) << 24) & 0xff000000L) | ((((long) header[9]) << 16) & 0xff0000L) | ((((long) header[10]) << 8) & 0xff00L) | ((long) header[11] & 0xffL); //OPTIONS + PADDING if (0 != optionsLength) { byte[] optBytes = new byte[optionsLength]; if (optionsLength != readFromStream(optBytes)) { streamInError = new IOException(JavaUtils.getMessage( "attach.DimeStreamError5", "" + optionsLength)); throw streamInError; } optBytes = null; //Yup throw it away, don't know anything about options. int pad = DimeBodyPart.dimePadding(optionsLength); if (pad != readFromStream(header, 0, pad)) { streamInError = new IOException( JavaUtils.getMessage("attach.DimeStreamError7")); throw streamInError; } } // ID + PADDING if (0 < idLength) { byte[] idBytes = new byte[ idLength]; if (idLength != readFromStream(idBytes)) { streamInError = new IOException( JavaUtils.getMessage("attach.DimeStreamError8")); throw streamInError; } if (idLength != 0 && !isChunk) { id = new String(idBytes); } int pad = DimeBodyPart.dimePadding(idLength); if (pad != readFromStream(header, 0, pad)) { streamInError = new IOException(JavaUtils.getMessage( "attach.DimeStreamError9")); throw streamInError; } } //TYPE + PADDING if (0 < typeLength) { byte[] typeBytes = new byte[typeLength]; if (typeLength != readFromStream(typeBytes)) { streamInError = new IOException(JavaUtils.getMessage( "attach.DimeStreamError10")); throw streamInError; } if (typeLength != 0 && !isChunk) { type = new String(typeBytes); } int pad = DimeBodyPart.dimePadding(typeLength); if (pad != readFromStream(header, 0, pad)) { streamInError = new IOException(JavaUtils.getMessage( "attach.DimeStreamError11")); throw streamInError; } } log.debug("MB:" + MB + ", ME:" + ME + ", CF:" + moreChunks + "Option length:" + optionsLength + ", ID length:" + idLength + ", typeLength:" + typeLength + ", TYPE_T:" + tnf); log.debug("id:\"" + id + "\""); log.debug("type:\"" + type + "\""); log.debug("recordlength:\"" + recordLength + "\""); dataPadLength = DimeBodyPart.dimePadding(recordLength); } /** * Read from the delimited stream. * @param b is the array to read into. Read as much as possible * into the size of this array. * @return the number of bytes read. -1 if endof stream. */ public int read(byte[] b) throws IOException { return read(b, 0, b.length); } /** * Read from the boundary delimited stream. * @return The byte read, or -1 if endof stream. */ public int read() throws IOException { byte[] b = new byte[1]; int read = read(b, 0, 1); if (read < 0) return -1; else return b[0]; } /** * Closes the stream. */ public synchronized void close() throws IOException { if (closed) return; closed = true; //mark it closed. log.debug(JavaUtils.getMessage("bStreamClosed", "" + streamNo)); if (bytesRead < recordLength || moreChunks) { //We need get this off the stream. //Easy way to flush through the stream; byte[] readrest = new byte[1024 * 16]; int bread = 0; do { bread = _read(readrest, 0, readrest.length); } while (bread > -1); } dataPadLength -= readPad(dataPadLength); } /** * mark the stream. * This is not supported. */ public void mark(int readlimit) {//do nothing } /** * reset the stream. * This is not supported. */ public void reset() throws IOException { streamInError = new IOException(JavaUtils.getMessage( "attach.bounday.mns")); throw streamInError; } /** * markSupported * return false; */ public boolean markSupported() { return false; } public synchronized int available() throws IOException { if (null != streamInError) throw streamInError; int chunkAvail = (int) Math.min((long) Integer.MAX_VALUE, recordLength - bytesRead); int streamAvail = 0; try { streamAvail = is.available(); } catch (IOException e) { streamInError = e; throw e; }; if (chunkAvail == 0 && moreChunks && (12 + dataPadLength) <= streamAvail) { dataPadLength -= readPad(dataPadLength); readHeader(true); return available(); } return Math.min(streamAvail, chunkAvail); } } 1.1 xml-axis/java/src/org/apache/axis/attachments/DimeMultiPart.java Index: DimeMultiPart.java =================================================================== /* * The Apache Software License, Version 1.1 * * * Copyright (c) 2001 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 "Axis" 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 [EMAIL PROTECTED] * * 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. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. */ /** * @author Rick Rineholt */ package org.apache.axis.attachments; import org.apache.axis.transport.http.HTTPConstants; import javax.activation.*; /** * This class hold all parts of a DIME multipart message. */ public final class DimeMultiPart { static final long transSize = Integer.MAX_VALUE; static final byte CURRENT_VERSION = 1; //Anything above this we don't support. protected java.util.Vector parts = new java.util.Vector(); public DimeMultiPart() {}; public void addBodyPart(DimeBodyPart part) { parts.add(part); } public void write(java.io.OutputStream os) throws java.io.IOException { int size = parts.size(); int last = size - 1; for (int i = 0; i < size; ++i) ((DimeBodyPart) parts.elementAt(i)).write(os, (byte) ((i == 0 ? DimeBodyPart.POSITION_FIRST : (byte) 0) | (i == last ? DimeBodyPart.POSITION_LAST : (byte) 0)), transSize); } public long getTransmissionSize() { long size = 0; for (int i = parts.size() - 1; i > -1; --i) size += ((DimeBodyPart) parts.elementAt(i)).getTransmissionSize( transSize); return size; } } 1.1 xml-axis/java/src/org/apache/axis/attachments/DimeTypeNameFormat.java Index: DimeTypeNameFormat.java =================================================================== /* * The Apache Software License, Version 1.1 * * * Copyright (c) 2001 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 "Axis" 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 [EMAIL PROTECTED] * * 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. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. */ /** * @author Rick Rineholt */ package org.apache.axis.attachments; import org.apache.axis.transport.http.HTTPConstants; import javax.activation.*; import org.apache.axis.utils.JavaUtils; /** * This class is a single part for DIME mulitpart message. */ public final class DimeTypeNameFormat { private byte format = 0; private DimeTypeNameFormat() {}; private DimeTypeNameFormat(byte f) { format = f; }; //Current type values. final static byte NOCHANGE_VALUE = 0x00; // indicates the type is unchanged from the previous record (used for chunking) final static byte MIME_VALUE = 0x01; //indicates the type is specified as a MIME media-type final static byte URI_VALUE = 0x02; // indicates the type is specified as an absolute URI final static byte UNKNOWN_VALUE = 0x03; // indicates the type is not specified final static byte NODATA_VALUE = 0x04; // indicates the record has no payload static final DimeTypeNameFormat NOCHANGE = new DimeTypeNameFormat(NOCHANGE_VALUE); public static final DimeTypeNameFormat MIME= new DimeTypeNameFormat(MIME_VALUE); public static final DimeTypeNameFormat URI= new DimeTypeNameFormat(URI_VALUE); public static final DimeTypeNameFormat UNKNOWN= new DimeTypeNameFormat(UNKNOWN_VALUE); static final DimeTypeNameFormat NODATA= new DimeTypeNameFormat(NODATA_VALUE); private static String[] toEnglish = {"NOCHANGE", "MIME", "URI", "UNKNOWN", "NODATA"}; private static DimeTypeNameFormat[] fromByte = {NOCHANGE, MIME, URI, UNKNOWN, NODATA}; public final String toString() { return toEnglish[format]; } public final byte toByte() { return format; } public final boolean equals(final Object x) { if (x == null) return false; if (!(x instanceof DimeTypeNameFormat)) return false; return ((DimeTypeNameFormat) x).format == this.format; } public static DimeTypeNameFormat parseByte(byte x) { if (x < 0 || x > fromByte.length) throw new IllegalArgumentException(JavaUtils.getMessage( "attach.DimeStreamBadType", "" + x)); return fromByte[x]; } public static DimeTypeNameFormat parseByte(Byte x) { return parseByte(x.byteValue()); } } 1.1 xml-axis/java/src/org/apache/axis/attachments/MultiPartDimeInputStream.java Index: MultiPartDimeInputStream.java =================================================================== /* * The Apache Software License, Version 1.1 * * * Copyright (c) 2001 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 "Axis" 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 [EMAIL PROTECTED] * * 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. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. */ package org.apache.axis.attachments; import org.apache.axis.transport.http.HTTPConstants; import org.apache.axis.attachments.ManagedMemoryDataSource; import javax.activation.DataHandler; import org.apache.axis.Part; import org.apache.axis.utils.JavaUtils; import javax.mail.internet.MimeUtility; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * * @author Rick Rineholt */ /** This simulates the multipart stream * */ public class MultiPartDimeInputStream extends MultiPartInputStream { protected static Log log = LogFactory.getLog(MultiPartDimeInputStream.class.getName()); protected java.util.HashMap parts = new java.util.HashMap(); protected java.util.LinkedList orderedParts = new java.util.LinkedList(); protected int rootPartLength = 0; protected boolean closed = false; //If true the stream has been closed. protected boolean eos = false; //This is set once the SOAP packet has reached the end of stream. //This stream controls and manages the boundary. protected DimeDelimitedInputStream dimeDelimitedStream = null; protected java.io.InputStream soapStream = null; //Set the soap stream once found. protected byte[] boundary = null; protected java.io.ByteArrayInputStream cachedSOAPEnvelope = null; //Caches the soap stream if it is //Still open and a reference to read data in a later attachment occurs. protected String contentId = null; /** * Multipart stream. * @param the string that holds the contentType * @param is the true input stream from where the source. */ public MultiPartDimeInputStream (java.io.InputStream is) throws java.io.IOException { super(null); //don't cache this stream. soapStream = dimeDelimitedStream = new DimeDelimitedInputStream(is); //The Soap stream must always be first contentId = dimeDelimitedStream.getContentId(); } public Part getAttachmentByReference(final String[] id) throws org.apache.axis.AxisFault { // if CID should still have CID: prefix. //First see if we have read it in yet. Part ret = null; try { for (int i = id.length - 1; ret == null && i > -1; --i) { ret = (AttachmentPart) parts.get(id[i]); } if (null == ret) { ret = readTillFound(id); } log.debug(JavaUtils.getMessage("return02", "getAttachmentByReference(\"" + id + "\"", (ret == null ? "null" : ret.toString()))); } catch (java.io.IOException e) { throw new org.apache.axis.AxisFault(e.getClass().getName() + e.getMessage()); } return ret; } protected void addPart(String contentId, String locationId, AttachmentPart ap) { if (contentId != null && contentId.trim().length() != 0) parts.put(contentId, ap); if (locationId != null && locationId.trim().length() != 0)parts.put(locationId, ap); orderedParts.add(ap); } protected final static String[] READ_ALL = { " * \0 ".intern()}; //Shouldn't never match protected void readAll() throws org.apache.axis.AxisFault { try { readTillFound(READ_ALL); } catch (Exception e) { throw org.apache.axis.AxisFault.makeFault(e); } } public java.util.Collection getAttachments() throws org.apache.axis.AxisFault { readAll(); return orderedParts; } /** * This will read streams in till the one that is needed is found. * @param The id is the stream being sought. TODO today its only handles CID. all ContentId streams * should be prefixed by "cid:" */ protected Part readTillFound(final String[] id) throws java.io.IOException { if (dimeDelimitedStream == null) return null; //The whole stream has been consumed already Part ret = null; try { if (soapStream != null) { //Still on the SOAP stream. if (!eos) { //The SOAP packet has not been fully read yet. Need to store it away. java.io.ByteArrayOutputStream soapdata = new java.io.ByteArrayOutputStream(1024 * 8); byte[] buf = new byte[1024 * 16]; int byteread = 0; do { byteread = soapStream.read(buf); if (byteread > 0) soapdata.write(buf, 0, byteread); } while (byteread > -1); soapdata.close(); soapStream.close(); soapStream = new java.io.ByteArrayInputStream( soapdata.toByteArray()); } dimeDelimitedStream = dimeDelimitedStream.getNextStream(); } //Now start searching for the data. if (null != dimeDelimitedStream) { do { String contentId = dimeDelimitedStream.getContentId(); String type = dimeDelimitedStream.getType(); if (type != null && !dimeDelimitedStream.getDimeTypeNameFormat().equals(DimeTypeNameFormat.MIME)) { type = "application/uri; uri=\"" + type + "\""; } DataHandler dh = new DataHandler( new ManagedMemoryDataSource(dimeDelimitedStream, 1024, type, true)); AttachmentPart ap = new AttachmentPart(dh); if (contentId != null) ap.addMimeHeader(HTTPConstants.HEADER_CONTENT_ID, contentId); addPart(contentId, "", ap); for (int i = id.length - 1; ret == null && i > -1; --i) { if (contentId != null && id[i].equals(contentId)) { //This is the part being sought ret = ap; } } dimeDelimitedStream = dimeDelimitedStream.getNextStream(); } while (null == ret && null != dimeDelimitedStream); } } catch (Exception e) { throw org.apache.axis.AxisFault.makeFault(e); } return ret; } /** * Return the content location. * @return the Content-Location of the stream. * Null if no content-location specified. */ public String getContentLocation() { return null; } /** * Return the content id of the stream * @return the Content-Location of the stream. * Null if no content-location specified. */ public String getContentId() { return contentId; } /** * Read the root stream. */ public int read(byte[] b, int off, int len) throws java.io.IOException { if (closed) { throw new java.io.IOException(JavaUtils.getMessage( "streamClosed")); } if (eos) { return -1; } int read = soapStream.read(b, off, len); if (read < 0) { eos = true; } return read; } public int read(byte[] b) throws java.io.IOException { return read(b, 0, b.length); } public int read() throws java.io.IOException { if (closed) { throw new java.io.IOException(JavaUtils.getMessage( "streamClosed")); } if (eos) { return -1; } int ret = soapStream.read(); if (ret < 0) { eos = true; } return ret; } public void close() throws java.io.IOException { closed = true; soapStream.close(); } } 1.1 xml-axis/java/src/org/apache/axis/attachments/MultiPartInputStream.java Index: MultiPartInputStream.java =================================================================== /* * The Apache Software License, Version 1.1 * * * Copyright (c) 2001 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 "Axis" 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 [EMAIL PROTECTED] * * 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. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. */ package org.apache.axis.attachments; import org.apache.axis.transport.http.HTTPConstants; import org.apache.axis.attachments.ManagedMemoryDataSource; import javax.activation.DataHandler; import org.apache.axis.Part; import org.apache.axis.utils.JavaUtils; import javax.mail.internet.MimeUtility; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * * @author Rick Rineholt */ /** This simulates the multipart stream * */ public abstract class MultiPartInputStream extends java.io.FilterInputStream { MultiPartInputStream(java.io.InputStream is) { super(is); } public abstract Part getAttachmentByReference(final String[] id) throws org.apache.axis.AxisFault; // if CID should still have CID: prefix. public abstract java.util.Collection getAttachments() throws org.apache.axis.AxisFault; /** * Return the content location. * @return the Content-Location of the stream. * Null if no content-location specified. */ public abstract String getContentLocation(); /** * Return the content id of the stream * @return the Content-Location of the stream. * Null if no content-location specified. */ public abstract String getContentId(); } 1.151 +48 -0 xml-axis/java/src/org/apache/axis/client/Call.java Index: Call.java =================================================================== RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/client/Call.java,v retrieving revision 1.150 retrieving revision 1.151 diff -u -r1.150 -r1.151 --- Call.java 29 Jun 2002 17:42:19 -0000 1.150 +++ Call.java 30 Jun 2002 19:59:04 -0000 1.151 @@ -132,6 +132,7 @@ * SEND_TYPE_ATTR - Should we send the XSI type attributes (true/false) * TIMEOUT - Timeout used by transport sender in seconds * TRANSPORT_NAME - Name of transport handler to use + * ATTACHMENT_ENCAPSULATION_FORMAT- Send attachments as MIME the default, or DIME. * </pre> * * @author Doug Davis ([EMAIL PROTECTED]) @@ -198,6 +199,22 @@ public static final boolean FAULT_ON_NO_RESPONSE = false; /** + * Property for setting attachment format. + */ + public static final String ATTACHMENT_ENCAPSULATION_FORMAT= + "attachment_encapsulation_format"; + /** + * Property value for setting attachment format as MIME. + */ + public static final String ATTACHMENT_ENCAPSULATION_FORMAT_MIME= + "axis.attachment.style.mime"; + /** + * Property value for setting attachment format as DIME. + */ + public static final String ATTACHMENT_ENCAPSULATION_FORMAT_DIME= + "axis.attachment.style.dime"; + + /** * A Hashtable mapping protocols (Strings) to Transports (classes) */ private static Hashtable transports = new Hashtable(); @@ -350,6 +367,19 @@ if (transport != null) transport.setTransportName((String) value); } + else if ( name.equals(ATTACHMENT_ENCAPSULATION_FORMAT) ) { + if (!(value instanceof String)) { + throw new IllegalArgumentException( + JavaUtils.getMessage("badProp00", new String[] { + name, "java.lang.String", value.getClass().getName()})); + } + if(!value.equals(ATTACHMENT_ENCAPSULATION_FORMAT_MIME ) && + !value.equals(ATTACHMENT_ENCAPSULATION_FORMAT_DIME )) + throw new IllegalArgumentException( + JavaUtils.getMessage("badattachmenttypeerr", new String[] { + (String) value, ATTACHMENT_ENCAPSULATION_FORMAT_MIME + " " + +ATTACHMENT_ENCAPSULATION_FORMAT_DIME })); + } else { throw new IllegalArgumentException( JavaUtils.getMessage("badProp05", name)); @@ -410,11 +440,13 @@ propertyNames.add(USERNAME_PROPERTY); propertyNames.add(PASSWORD_PROPERTY); propertyNames.add(SESSION_MAINTAIN_PROPERTY); + propertyNames.add(ATTACHMENT_ENCAPSULATION_FORMAT); propertyNames.add(OPERATION_STYLE_PROPERTY); propertyNames.add(SOAPACTION_USE_PROPERTY); propertyNames.add(SOAPACTION_URI_PROPERTY); propertyNames.add(ENCODINGSTYLE_URI_PROPERTY); propertyNames.add(TRANSPORT_NAME); + propertyNames.add(ATTACHMENT_ENCAPSULATION_FORMAT); } public Iterator getPropertyNames() { @@ -1483,6 +1515,22 @@ * @param msg the new request message. */ public void setRequestMessage(Message msg) { + String attachformat= (String)getProperty( + ATTACHMENT_ENCAPSULATION_FORMAT); + + if(null != attachformat){ + org.apache.axis.attachments.Attachments attachments= + msg.getAttachmentsImpl(); + if(null != attachments) { + if( null != attachformat && attachformat.equals( + ATTACHMENT_ENCAPSULATION_FORMAT_MIME)) + attachments.setSendType(attachments.SEND_TYPE_MIME); + else if( null != attachformat && attachformat.equals( + ATTACHMENT_ENCAPSULATION_FORMAT_DIME)) { + attachments.setSendType(attachments.SEND_TYPE_DIME); + } + } + } if(null != attachmentParts && !attachmentParts.isEmpty()){ try{ 1.120 +1 -1 xml-axis/java/src/org/apache/axis/transport/http/AxisServlet.java Index: AxisServlet.java =================================================================== RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/transport/http/AxisServlet.java,v retrieving revision 1.119 retrieving revision 1.120 diff -u -r1.119 -r1.120 --- AxisServlet.java 27 Jun 2002 22:44:13 -0000 1.119 +++ AxisServlet.java 30 Jun 2002 19:59:04 -0000 1.120 @@ -643,7 +643,7 @@ res.setStatus(HttpServletResponse.SC_NO_CONTENT); if(isDebug) log.debug("NO AXIS MESSAGE TO RETURN!"); //String resp = JavaUtils.getMessage("noData00"); - //res.setContentLength(resp.getBytes().length); + //res.setContentLength((int) resp.getBytes().length); //res.getWriter().print(resp); } else { if(isDebug) { 1.53 +9 -0 xml-axis/java/src/org/apache/axis/utils/JavaUtils.java Index: JavaUtils.java =================================================================== RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/utils/JavaUtils.java,v retrieving revision 1.52 retrieving revision 1.53 diff -u -r1.52 -r1.53 --- JavaUtils.java 28 Jun 2002 06:17:54 -0000 1.52 +++ JavaUtils.java 30 Jun 2002 19:59:04 -0000 1.53 @@ -720,6 +720,15 @@ } // getMessage /** + * Get the message with the given key. If arguments are specified in the message (in the + * format of "{0} {1}") then fill them in with the values of var1 and var2, respectively. + */ + public static String getMessage(String key, String var1, String var2, String var3) + throws MissingResourceException { + return MessageFormat.format(getMessage(key), new String[]{var1, var2, var3}); + } // getMessage + + /** * Get the message with the given key. Replace each "{X}" in the message with vars[X]. If * there are more vars than {X}'s, then the extra vars are ignored. If there are more {X}'s * than vars, then a java.text.ParseException (subclass of RuntimeException) is thrown. 1.20 +26 -0 xml-axis/java/src/org/apache/axis/utils/axisNLS.properties Index: axisNLS.properties =================================================================== RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/utils/axisNLS.properties,v retrieving revision 1.19 retrieving revision 1.20 diff -u -r1.19 -r1.20 --- axisNLS.properties 29 Jun 2002 18:05:19 -0000 1.19 +++ axisNLS.properties 30 Jun 2002 19:59:04 -0000 1.20 @@ -909,4 +909,30 @@ optionNoWrap00=turn off support for "wrapped" document/literal noTypeOrElement00=Error: Message part {0} of operation or fault {1} has no element or type attribute. +msgContentLengthHTTPerr=Message content length {0} exceeds servlet return capacity. +badattachmenttypeerr=The value of {0} for attachment format must be {1}; +attach.dimetypeexceedsmax=DIME Type length is {0} which exceeds maximum {0} +attach.dimelengthexceedsmax=DIME ID length is {0} which exceeds maximum {1}. +attach.dimeMaxChunkSize0=Max chunk size \"{0}\" needs to be greater than one. +attach.dimeMaxChunkSize1=Max chunk size \"{0}\" exceeds 32 bits. +attach.dimeReadFullyError=Each DIME Stream must be read fully or closed in succession. +attach.dimeNotPaddedCorrectly=DIME stream data not padded correctly. +attach.readLengthError=Received \"{0}\" bytes to read. +attach.readOffsetError=Received \"{0}\" as an offset. +attach.readArrayNullError=Array to read is null +attach.readArrayNullError=Array to read is null +attach.readArraySizeError=Array size of {0} to read {1} at offset {2} is too small. +attach.DimeStreamError0=End of physical stream detected when more DIME chunks expected. +attach.DimeStreamError1=End of physical stream detected when {0} more bytes expected. +attach.DimeStreamError2=There are no more DIME chunks expected! +attach.DimeStreamError3=DIME header less than {0} bytes. +attach.DimeStreamError4=DIME version received \"{0}\" greater than current supported version \"{1}\". +attach.DimeStreamError5=DIME option length \"{0}\" is greater stream length. +attach.DimeStreamError6=DIME typelength length \"{0}\" is greater stream length. +attach.DimeStreamError7=DIME stream closed during options padding. +attach.DimeStreamError8=DIME stream closed getting ID length. +attach.DimeStreamError9=DIME stream closed getting ID padding. +attach.DimeStreamError10=DIME stream closed getting type. +attach.DimeStreamError11=DIME stream closed getting type padding. +attach.DimeStreamBadType=DIME stream received bad type \"{0}\".