axis-java-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From rineh...@apache.org
Subject cvs commit: xml-axis/java/test/md5attach MD5AttachTest.java
Date Tue, 06 Nov 2001 20:06:02 GMT
rineholt    01/11/06 12:06:02

  Modified:    java     build.xml
               java/src/org/apache/axis Message.java SOAPPart.java
               java/src/org/apache/axis/attachments AttachmentPart.java
                        Attachments.java AttachmentsImpl.java
               java/src/org/apache/axis/configuration FileProvider.java
               java/src/org/apache/axis/message MessageElement.java
               java/src/org/apache/axis/transport/http AxisServlet.java
                        HTTPSender.java
  Added:       java/src/org/apache/axis/attachments AttachmentUtils.java
                        BoundaryDelimitedStream.java
                        ManagedMemoryDataSource.java MimeUtils.java
                        MultiPartRelatedInputStream.java
               java/src/org/apache/axis/handlers MD5AttachHandler.java
               java/test/md5attach MD5AttachTest.java
  Log:
  The beginning of getting SOAP attachment stream to the Axis engine.
  
  Revision  Changes    Path
  1.79      +21 -3     xml-axis/java/build.xml
  
  Index: build.xml
  ===================================================================
  RCS file: /home/cvs/xml-axis/java/build.xml,v
  retrieving revision 1.78
  retrieving revision 1.79
  diff -u -r1.78 -r1.79
  --- build.xml	2001/11/06 15:40:07	1.78
  +++ build.xml	2001/11/06 20:06:01	1.79
  @@ -128,6 +128,17 @@
         classname="javax.activation.DataHandler"
         classpathref="classpath"/>
   
  +    <available property="mailapi.present"
  +      classname="javax.mail.internet.MimeMessage"
  +      classpathref="classpath"/>
  +
  +    <condition property="attachments.present" >
  +      <and>
  +        <available classname="javax.activation.DataHandler" classpathref="classpath" />
  +        <available classname="javax.mail.internet.MimeMessage" classpathref="classpath" />
  +      </and>
  +    </condition>
  +
       <available property="post-compile.present" file="post-compile.xml" />
   
       <echo message="--- Build environment for ${Name} ---" />
  @@ -143,6 +154,8 @@
       <echo message="servlet.present=${servlet.present}" />
       <echo message="junit.present=${junit.present}" />
       <echo message="activation.present=${activation.present}" />
  +    <echo message="mailapi.present=${mailapi.present}" />
  +    <echo message="attachments.present=${attachments.present}" />
       <echo message=""/>
       <echo message="--- Property values ---" />
       <echo message="debug=${debug}" />
  @@ -164,9 +177,13 @@
         classpathref="classpath">
         <exclude name="**/old/**/*" />
         <exclude name="**/bak/**"/>
  -      <exclude name="org/apache/axis/attachments/AttachmentsImpl.java" unless="activation.present"/>
  -      <exclude name="org/apache/axis/attachments/AttachmentPart.java" unless="activation.present"/>
  -      <exclude name="org/apache/axis/attachments/MimeUtil.java" unless="activation.present"/>
  +      <exclude name="org/apache/axis/attachments/AttachmentsImpl.java" unless="attachments.present"/>
  +      <exclude name="org/apache/axis/attachments/AttachmentPart.java" unless="attachments.present"/>
  +      <exclude name="org/apache/axis/attachments/AttachmentUtils.java" unless="attachments.present"/>
  +      <exclude name="org/apache/axis/attachments/MimeUtils.java" unless="attachments.present"/>
  +      <exclude name="org/apache/axis/attachments/ManagedMemoryDataSource.java" unless="attachments.present"/>
  +      <exclude name="org/apache/axis/handlers/MD5AttachHandler.java" unless="attachments.present"/>
  +      <exclude name="org/apache/axis/attachments/MultiPartRelatedInputStream.java" unless="attachments.present"/>
         <exclude name="org/apache/axis/transport/http/AdminServlet.java" unless="servlet.present"/>
         <exclude name="org/apache/axis/transport/http/AxisHttpSession.java" unless="servlet.present"/>
         <exclude name="org/apache/axis/transport/http/AxisServlet.java" unless="servlet.present"/>
  @@ -241,6 +258,7 @@
         <exclude name="test/inout/*.java" />
         <exclude name="test/multiref/*.java" />
         <exclude name="test/wsdl/Wsdl2javaTestSuite.java" unless="servlet.present"/>
  +      <exclude name="test/md5attach/MD5AttachTest.java" unless="attachments.present"/>
       </javac>
     </target>
   
  
  
  
  1.51      +89 -33    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.50
  retrieving revision 1.51
  diff -u -r1.50 -r1.51
  --- Message.java	2001/11/02 03:07:40	1.50
  +++ Message.java	2001/11/06 20:06:01	1.51
  @@ -74,6 +74,7 @@
    * @author Rob Jellinghaus (robj@unrealities.com)
    * @author Doug Davis (dug@us.ibm.com)
    * @author Glen Daniels (gdaniels@allaire.com)
  + * @author Rick Rineholt 
    */
   public class Message {
       static Category category =
  @@ -81,6 +82,10 @@
   
       public static final String REQUEST  = "request" ;
       public static final String RESPONSE = "response" ;
  +    //MIME parts defined for messages.
  +    public static final String MIME_MULTIPART_RELATED="multipart/related";
  +    public static final String MIME_APPLICATION_DIME="application/dime"; //NOT SUPPORTED NOW
  +    public static final String MIME_UNKNOWN="  "; // look at the input stream to find the headers to decide.
   
       /**
        * The messageType indicates whether this is request or response.
  @@ -96,7 +101,7 @@
        * This Message's Attachments object, which manages the attachments
        * contained in this Message.
        */
  -    private Attachments mAttachments;
  +    private Attachments mAttachments= null;
       
       /**
        * The MessageContext we are associated with.
  @@ -123,12 +128,20 @@
       }
       
       /**
  -     * Get this message's Content-Length in bytes (which will include any
  -     * MIME part dividers, but will not include any transport headers).
  +     * Construct a Message, using the provided initialContents as the
  +     * contents of the Message's SOAPPart.
  +     * <p>
  +     * Eventually, genericize this to
  +     * return the RootPart instead, which will have some kind of
  +     * EnvelopeFactory to enable support for things other than SOAP.
  +     * But that all will come later, with lots of additional refactoring.
  +     *
  +     * @param initialContents may be String, byte[], InputStream, SOAPEnvelope, or AxisFault.
  +     * @param bodyInStream is true if initialContents is an InputStream containing just
  +     * the SOAP body (no SOAP-ENV).
        */
  -    public int getContentLength () {
  -        // TODO: something real!
  -		return mSOAPPart.getContentLength();
  +    public Message(Object initialContents, boolean bodyInStream) {
  +      this(initialContents, bodyInStream, (String)null );
       }
       
       /**
  @@ -142,10 +155,11 @@
   	 *
   	 * @param initialContents may be String, byte[], InputStream, SOAPEnvelope, or AxisFault.
        * @param bodyInStream is true if initialContents is an InputStream containing just
  +     * @param contentType this if the contentType has been already determined.  (as in the case of servlets); 
        * the SOAP body (no SOAP-ENV).
        */
  -    public Message (Object initialContents, boolean bodyInStream) {
  -		setup(initialContents, bodyInStream);
  +    public Message(Object initialContents, boolean bodyInStream, String contentType) {
  +		setup(initialContents, bodyInStream, contentType);
       }
   	
   	/**
  @@ -153,38 +167,42 @@
   	 * defaulting bodyInStream to false.
   	 */
   	public Message (Object initialContents) {
  -		setup(initialContents, false);
  +		setup(initialContents, false, null);
   	}
       
   	/**
   	 * Do the work of construction.
   	 */
  -	private void setup (Object initialContents, boolean bodyInStream) {
  -        mSOAPPart = new SOAPPart(this, initialContents, bodyInStream);
  +	private void setup (Object initialContents, boolean bodyInStream, String contentType) {
           
  -        // Try to construct an AttachmentsImpl object for attachment functionality.
  -        // If there is no org.apache.axis.attachments.AttachmentsImpl class,
  -        // it must mean activation.jar is not present and attachments are not
  -        // supported.
  -        try {
  -            Class attachImpl = Class.forName("org.apache.axis.attachments.AttachmentsImpl");
  -            // Construct one, and cast to Attachments.
  -            // There must be exactly one constructor of AttachmentsImpl, which must
  -            // take an org.apache.axis.Message!
  -            Constructor attachImplConstr = attachImpl.getConstructors()[0];
  -            Object[] args = new Object[1];
  -            args[0] = this;
  -            mAttachments = (Attachments)attachImplConstr.newInstance(args);
  -        } catch (ClassNotFoundException ex) {
  -            // no support for it, leave mAttachments null.
  -        } catch (InvocationTargetException ex) {
  -            // no support for it, leave mAttachments null.
  -        } catch (InstantiationException ex) {
  -            // no support for it, leave mAttachments null.
  -        } catch (IllegalAccessException ex) {
  -            // no support for it, leave mAttachments null.
  +          // Try to construct an AttachmentsImpl object for attachment functionality.
  +          // If there is no org.apache.axis.attachments.AttachmentsImpl class,
  +          // it must mean activation.jar is not present and attachments are not
  +          // supported.
  +          try {
  +              Class attachImpl = Class.forName("org.apache.axis.attachments.AttachmentsImpl");
  +              // Construct one, and cast to Attachments.
  +              // There must be exactly one constructor of AttachmentsImpl, which must
  +              // take an org.apache.axis.Message!
  +              Constructor attachImplConstr = attachImpl.getConstructors()[0];
  +              mAttachments = (Attachments)attachImplConstr.newInstance(new Object[]{this,initialContents, contentType});
  +
  +              mSOAPPart = (SOAPPart) mAttachments.getRootPart(); //If it can't support it, it wont have a root part.
  +              
  +          } catch (ClassNotFoundException ex) {
  +              // no support for it, leave mAttachments null.
  +          } catch (InvocationTargetException ex) {
  +              // no support for it, leave mAttachments null.
  +          } catch (InstantiationException ex) {
  +              // no support for it, leave mAttachments null.
  +          } catch (IllegalAccessException ex) {
  +              // no support for it, leave mAttachments null.
  +          }
  +
  +        if(null == mSOAPPart ){ //The stream was not determined by a more complex type so default to text/xml 
  +          mSOAPPart = new SOAPPart(this, initialContents, bodyInStream);
           }
  -	}
  +     }
   	
       /**
        * Get this message's SOAPPart.
  @@ -207,4 +225,42 @@
       public Attachments getAttachments () {
           return mAttachments;
       }
  +
  +  public String getContentType() throws org.apache.axis.AxisFault {
  +    String ret= "text/xml; charset=utf-8";
  +    if(mAttachments != null && 0 != mAttachments.getAttachmentCount()){
  +        ret= mAttachments.getContentType();
  +    }
  +    return ret;
  +  }
  +  public int getContentLength() throws org.apache.axis.AxisFault{ //This will have to give way someday to HTTP Chunking but for now kludge.
  +    int ret= 0; 
  +    if(mAttachments == null ||   0== mAttachments.getAttachmentCount()){
  +      ret= mSOAPPart.getAsBytes().length; 
  +    }else{
  +        ret= mAttachments.getContentLength();
  +    }
  +    return ret;
  +  }
  +
  +
  +  public void writeContentToStream(java.io.OutputStream os){
  +
  +    if(mAttachments == null || 0== mAttachments.getAttachmentCount()){ //Do it the old fashion way.
  +      try{
  +          os.write(mSOAPPart.getAsBytes());
  +      }catch( java.io.IOException e){
  +        System.err.println(e);
  +        e.printStackTrace();
  +      }
  +    }else{
  +      try{
  +          mAttachments.writeContentToStream(os);
  +      }catch(java.lang.Exception e){
  +        System.err.println(e);
  +        e.printStackTrace();
  +      }
  +    }
  +  }
  +  
   }
  
  
  
  1.3       +40 -21    xml-axis/java/src/org/apache/axis/SOAPPart.java
  
  Index: SOAPPart.java
  ===================================================================
  RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/SOAPPart.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- SOAPPart.java	2001/11/06 14:09:07	1.2
  +++ SOAPPart.java	2001/11/06 20:06:01	1.3
  @@ -59,7 +59,6 @@
   import org.apache.axis.encoding.SerializationContext;
   import org.apache.axis.message.InputStreamBody;
   import org.apache.axis.message.SOAPEnvelope;
  -import org.apache.axis.utils.JavaUtils;
   import org.apache.log4j.Category;
   import org.xml.sax.InputSource;
   import org.xml.sax.SAXException;
  @@ -133,14 +132,16 @@
               form = FORM_FAULT;
           }
           if (category.isDebugEnabled()) {
  -            category.debug(JavaUtils.getMessage("enter00", "SOAPPart ctor (" + formNames[form] + ")"));
  +            category.debug( "Enter SOAPPart ctor ("+formNames[form]+")" );
           }
           setCurrentMessage(initialContents, form);
       }
  +    /* This could be rather costly with attachments.  
   
       public Object getOriginalMessage() {
           return( originalMessage );
       }
  +    */
   
       /**
        * Content type is always "text/xml" for SOAPParts.
  @@ -158,6 +159,21 @@
           byte[] bytes = this.getAsBytes();
           return bytes.length;
       }
  +    /**
  +     * This set the SOAP Envelope for this part. 
  +     * 
  +     * Note: It breaks the chicken/egg created.
  +     *  I need a message to create an attachment...
  +     *  From the attachment I should be able to get a reference...
  +     *  I now want to edit elements in the envelope in order to
  +     *    place the  attachment reference to it.
  +     *  How do I now update the SOAP envelope with what I've changed?
  +     *  
  +     */
  +
  +    public void setSOAPEnvelope(org.apache.axis.message.SOAPEnvelope env){
  +       setCurrentMessage(env, FORM_SOAPENVELOPE) ;
  +    }
   
       /**
        * Get the total size in bytes, including headers, of this Part.
  @@ -199,7 +215,9 @@
        */
       private void setCurrentMessage(Object currMsg, int form) {
           if (category.isDebugEnabled()) {
  -            category.debug(JavaUtils.getMessage("setCurrMsg00", formNames[form], currMsg.toString()));
  +            category.debug( "Setting current message form to: " +
  +                        formNames[form] +" (currentMessage is now " +
  +                        currMsg + ")" );
           }
           currentMessage = currMsg ;
           currentForm = form ;
  @@ -210,9 +228,9 @@
        * array.  This will force buffering of the message.
        */
       public byte[] getAsBytes() {
  -        category.debug(JavaUtils.getMessage("enter00", "SOAPPart::getAsBytes"));
  +        category.debug( "Enter: SOAPPart::getAsBytes" );
           if ( currentForm == FORM_BYTES ) {
  -            category.debug(JavaUtils.getMessage("exit00", "SOAPPart::getAsBytes"));
  +            category.debug( "Exit: SOAPPart::getAsBytes" );
               return( (byte[]) currentMessage );
           }
   
  @@ -220,7 +238,7 @@
               try {
                   getAsSOAPEnvelope();
               } catch (Exception e) {
  -                category.fatal(JavaUtils.getMessage("makeEnvFail00"), e);
  +                category.fatal("Couldn't make envelope", e);
                   return null;
               }
           }
  @@ -239,13 +257,13 @@
                   // byte[]  buf = new byte[ len ];
                   // inp.read( buf );
                   setCurrentMessage( buf, FORM_BYTES );
  -                category.debug(JavaUtils.getMessage("exit00", "SOAPPart::getAsByes"));
  +                category.debug( "Exit: SOAPPart::getAsByes" );
                   return( (byte[]) currentMessage );
               }
               catch( Exception e ) {
                   e.printStackTrace( System.err );
               }
  -            category.debug(JavaUtils.getMessage("exit00", "SOAPPart::getAsByes"));
  +            category.debug( "Exit: SOAPPart::getAsByes" );
               return( null );
           }
   
  @@ -256,12 +274,12 @@
           if ( currentForm == FORM_STRING ) {
               setCurrentMessage( ((String)currentMessage).getBytes(),
                                  FORM_BYTES );
  -            category.debug(JavaUtils.getMessage("exit00", "SOAPPart::getAsByes"));
  +            category.debug( "Exit: SOAPPart::getAsBytes" );
               return( (byte[]) currentMessage );
           }
   
  -        System.err.println(JavaUtils.getMessage("cantConvert00", "" + currentForm));
  -        category.debug(JavaUtils.getMessage("exit00", "SOAPPart::getAsByes"));
  +        System.err.println("Can't convert " + currentForm + " to Bytes" );
  +        category.debug( "Exit: SOAPPart::getAsBytes" );
           return( null );
       }
   
  @@ -270,10 +288,10 @@
        * This will force buffering of the message.
        */
       public String getAsString() {
  -        category.debug(JavaUtils.getMessage("enter00", "SOAPPart::getAsString") );
  +        category.debug( "Enter: SOAPPart::getAsString" );
           if ( currentForm == FORM_STRING ) {
  -            category.debug( JavaUtils.getMessage("exit00", "SOAPPart::getAsString, currentMessage = " +
  -                            currentMessage) );
  +            category.debug( "Exit: SOAPPart::getAsString, currentMessage is "+
  +                            currentMessage );
               return( (String) currentMessage );
           }
   
  @@ -286,8 +304,8 @@
           if ( currentForm == FORM_BYTES ) {
               setCurrentMessage( new String((byte[]) currentMessage),
                                  FORM_STRING );
  -            category.debug( JavaUtils.getMessage("exit00", "SOAPPart::getAsString, currentMessage = " +
  -                            currentMessage) );
  +            category.debug( "Exit: SOAPPart::getAsString, currentMessage is "+
  +                            currentMessage );
               return( (String) currentMessage );
           }
   
  @@ -317,8 +335,9 @@
               return (String)currentMessage;
           }
   
  -        System.err.println(JavaUtils.getMessage("cantConvert01", "" + currentForm));
  -        category.debug(JavaUtils.getMessage("exit00", "SOAPPart::getAsString"));
  +        System.err.println("Can't convert form " + currentForm +
  +                           " to String" );
  +        category.debug( "Exit: SOAPPart::getAsString" );
           return( null );
       }
   
  @@ -330,8 +349,8 @@
       public SOAPEnvelope getAsSOAPEnvelope()
           throws AxisFault
       {
  -        category.debug( JavaUtils.getMessage("enter00", "SOAPPart::getAsSOAPEnvelope, currentForm = " +
  -                        formNames[currentForm]) );
  +        category.debug( "Enter: SOAPPart::getAsSOAPEnvelope; currentForm is "+
  +                        formNames[currentForm] );
           if ( currentForm == FORM_SOAPENVELOPE )
               return( (SOAPEnvelope) currentMessage );
   
  @@ -365,7 +384,7 @@
           }
   
           setCurrentMessage(dser.getEnvelope(), FORM_SOAPENVELOPE);
  -        category.debug( JavaUtils.getMessage("exit00", "SOAPPart::getAsSOAPEnvelope") );
  +        category.debug( "Exit: SOAPPart::getAsSOAPEnvelope" );
           return( (SOAPEnvelope) currentMessage );
       }
   
  
  
  
  1.2       +14 -2     xml-axis/java/src/org/apache/axis/attachments/AttachmentPart.java
  
  Index: AttachmentPart.java
  ===================================================================
  RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/attachments/AttachmentPart.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- AttachmentPart.java	2001/11/02 03:07:42	1.1
  +++ AttachmentPart.java	2001/11/06 20:06:01	1.2
  @@ -75,17 +75,29 @@
    * Attachment-aware code, of course, is no problem.
    * 
    * @author Rob Jellinghaus (robj@unrealities.com)
  - * @author Rick Rineholt (rineholt@us.ibm.com)
  + * @author Rick Rineholt 
    */
   public class AttachmentPart extends Part {
       static Category category = Category.getInstance(Message.class.getName());
  +    javax.activation.DataHandler datahandler= null;
       
       /**
        * Do not call this directly!  This should only be called by the
        * AttachmentsImpl object.
        */ 
  -    public AttachmentPart (Message parent) {
  +
  +     
  +    public AttachmentPart(Message parent) {
           super(parent);
  +    }
  +
  +    public AttachmentPart(Message parent, javax.activation.DataHandler dh ) {
  +        super(parent);
  +        datahandler= dh;
  +    }
  +
  +    javax.activation.DataHandler getActiviationDataHandler(){
  +      return datahandler;
       }
   
       /**
  
  
  
  1.2       +33 -11    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.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- Attachments.java	2001/11/02 03:07:42	1.1
  +++ Attachments.java	2001/11/06 20:06:02	1.2
  @@ -65,28 +65,50 @@
    * will be compiled in org.apache.axis.attachments.
    *
    * @author Rob Jellinghaus (robj@unrealities.com)
  + * @author Rick Rineholt
    */
   
   public interface Attachments {
       /**
  -     * Get a given attachment Part, by content id. 
  -     * Will actually, and always, return an AttachmentPart.
  +     * This method should look at a refernce and determine if it is a CID: or url
  +     * to look for attachment.
  +     * @param  The reference in the xml that referers to an attachment.
  +     * @return The part associated with the attachment.
        */ 
  -    public Part getAttachmentById (String contentId);
  +    public Part getAttachmentByReference(String reference) throws org.apache.axis.AxisFault;
       
       /**
  -     * Get a given attachment Part, by content location.
  +     * Create a new attachment Part in this Message.
        * Will actually, and always, return an AttachmentPart.
  +     * @param The part that is referenced 
        */ 
  -    public Part getAttachmentByLocation (String contentLocation);
  -    
  +    public Part createAttachmentPart(Object part) throws org.apache.axis.AxisFault;
  +
       /**
  -     * Create a new attachment Part in this Message.
  -     * Will actually, and always, return an AttachmentPart.
  +     * From the complex stream return the SOAP part. 
  +     * @return will return the root part if the stream is supported,
  +     *         otherwise null.
        */ 
  -    public Part createAttachmentPart ();
  -    
  +    public Part getRootPart();
  +
       /**
  -     * Coming soon: attachment enumeration!
  +     * Get the content length of the stream. 
        */ 
  +    public int getContentLength() throws org.apache.axis.AxisFault;
  +
  +    /**
  +     * Write the content to the stream. 
  +     */ 
  +    public void writeContentToStream(java.io.OutputStream os) throws org.apache.axis.AxisFault;
  +
  +    /**
  +     * Write the content to the stream. 
  +     */ 
  +    public String getContentType()throws org.apache.axis.AxisFault;
  +
  +    /**
  +     *This is the number of attachments.
  +     **/
  +    public int getAttachmentCount();
  +    
   }
  
  
  
  1.2       +106 -15   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.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- AttachmentsImpl.java	2001/11/02 03:07:42	1.1
  +++ AttachmentsImpl.java	2001/11/06 20:06:02	1.2
  @@ -53,48 +53,139 @@
    * <http://www.apache.org/>.
    */
   
  + /* @author Rob Jellinghaus (robj@unrealities.com) */
  + /* @author Rick Rineholt  */
  +
   package org.apache.axis.attachments;
   
   import org.apache.axis.Message;
   import org.apache.axis.Part;
   
  -import java.util.Hashtable;
   
   /**
  - * Implements the Attachment interface, via an actual Hashtable of actual
  + * Implements the Attachment interface, via an actual Hashmap of actual
    * AttachmentParts.
  - *
  - * @author Rob Jellinghaus (robj@unrealities.com)
    */
   
   public class AttachmentsImpl implements Attachments {
       private Message msg;
       
  -    private Hashtable attachments = new Hashtable();
       
  +    private java.util.HashMap attachments = new java.util.HashMap();
  +
  +    protected org.apache.axis.SOAPPart soapPart= null; 
       /**
  +     * The actual stream to manage the multi-related input stream.
  +     */
  +    protected org.apache.axis.attachments.MultiPartRelatedInputStream mpartStream= null;
  +    
  +    /**
        * Construct one of these on a parent Message.
        * Should only ever be called by Message constructor!
  +     * @param msg the message associated 
  +     * @param initialContents should be anything but today only a stream is supported.
  +     * @param The mime content type of the stream for transports that provide it.
        */ 
  -    public AttachmentsImpl (Message parent) {
  -        msg = parent;
  +    public AttachmentsImpl(Message msg, Object intialContents, String contentType) throws org.apache.axis.AxisFault {
  +      this.msg= msg;
  +      if(contentType  != null) {
  +        if(contentType.equals(org.apache.axis.Message.MIME_UNKNOWN)){
  +        //Process the input stream for headers to determine the mime type.
  +        //TODO
  +        }
  +        else{
  +          java.util.StringTokenizer st = new java.util.StringTokenizer(contentType, " \t;");
  +           if(st.hasMoreTokens()) {
  +               String mimetype= st.nextToken();
  +               if(mimetype.equalsIgnoreCase(org.apache.axis.Message.MIME_MULTIPART_RELATED)){
  +                 mpartStream= new org.apache.axis.attachments.MultiPartRelatedInputStream(contentType, (java.io.InputStream)intialContents);
  +
  +                soapPart= new org.apache.axis.SOAPPart(msg, mpartStream, false); 
  +               }
  +               else if(mimetype.equalsIgnoreCase(org.apache.axis.Message.MIME_APPLICATION_DIME)){ //do nothing today.
  +                 //is= new DIMEInputStreamManager(is);
  +               }
  +           }
  +         }
  +      }
  +    }
  +
  +    /**
  +     * Create an attachment part with a buried JAF data handler.
  +     */
  +    public Part createAttachmentPart(Object datahandler ) throws org.apache.axis.AxisFault{
  +       if(!( datahandler  instanceof javax.activation.DataHandler )){
  +            throw new org.apache.axis.AxisFault( "Unsupported attachment type \"" + datahandler.getClass().getName()
  +             + "\" only supporting \"" + javax.activation.DataHandler.class.getName() +"\".");
  +         }
  +        Part ret= new AttachmentPart(msg, (javax.activation.DataHandler)datahandler);
  +        String contentId= org.apache.axis.attachments.MimeUtils.getNewContentIdValue();
  +        ret.setContentId(contentId);  
  +        attachments.put(contentId, ret); 
  +        return ret;
       }
   
  -    public Part createAttachmentPart() {
  -        return new AttachmentPart(msg);
  +    /**
  +     * This method should look at a refernce and determine if it is a CID: or url
  +     * to look for attachment.
  +     * @param  The reference in the xml that referers to an attachment.
  +     * @return The part associated with the attachment.
  +     */ 
  +    public Part getAttachmentByReference(String reference) throws org.apache.axis.AxisFault {
  +        //TODO should find what type of reference it is .. today only Content Ids are handled.
  +        Part ret= (AttachmentPart)attachments.get(reference);
  +        if(ret == null && mpartStream != null ){
  +          //We need to still check if this coming in the input stream;
  +          javax.activation.DataHandler dh =mpartStream.getAttachmentByReference(reference); 
  +          if(dh != null){
  +            ret= new AttachmentPart(msg, dh);
  +          }
  +        }
  +        return  ret;
       }
   
       /**
  -     * TODO: everything!
  +     * From the complex stream return the root part. 
  +     * Today this is SOAP.
        */ 
  -    public Part getAttachmentById(String contentId) {
  -        return null;
  +    public Part getRootPart(){
  +      return soapPart;
       }
   
  +    public javax.mail.internet.MimeMultipart multipart= null; 
  +    
       /**
  -     * TODO: everything!
  +     * Get the content length of the stream. 
        */ 
  -    public Part getAttachmentByLocation(String contentLocation) {
  -        return null;
  +    public int getContentLength() throws org.apache.axis.AxisFault {
  +      try{
  +        return (int) org.apache.axis.attachments.MimeUtils.getContentLength( multipart !=null ? multipart :
  +        (multipart= org.apache.axis.attachments.MimeUtils.createMP(msg.getSOAPPart().getAsString(), attachments  )));
  +      }  
  +      catch(Exception e){
  +          throw new org.apache.axis.AxisFault();
  +      }
  +    }
  +
  +    /**
  +     * Write the content to the stream. 
  +     */ 
  +    public void writeContentToStream(java.io.OutputStream os) throws org.apache.axis.AxisFault {
  +         org.apache.axis.attachments.MimeUtils.writeToMultiPartStream(os, multipart !=null ? multipart :
  +      (multipart= org.apache.axis.attachments.MimeUtils.createMP(msg.getSOAPPart().getAsString(), attachments  )));
  +    }
  +    /**
  +     * Gets the content type for the whole stream.
  +     */
  +    public String getContentType()throws org.apache.axis.AxisFault {
  +       return org.apache.axis.attachments.MimeUtils.getContentType( multipart !=null ? multipart :
  +      (multipart= org.apache.axis.attachments.MimeUtils.createMP(msg.getSOAPPart().getAsString(), attachments  )));
  +    }
  +
  +    /**
  +     *This is the number of attachments.
  +     **/
  +    public int getAttachmentCount(){
  +       return attachments.size(); 
       }
   }
  
  
  
  1.1                  xml-axis/java/src/org/apache/axis/attachments/AttachmentUtils.java
  
  Index: AttachmentUtils.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 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.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  package org.apache.axis.attachments;
  import javax.activation.DataHandler;
  import org.apache.axis.Part;
  import org.apache.axis.AxisFault;
  
  
  /**
   * This class allow access to the Jaf data handler in AttachmentPart. 
   *
   * @author Rick Rineholt
   */
  
  public class AttachmentUtils {
      private AttachmentUtils(){};  //no one should create.
  
      /**
       * Obtain the DataHandler from the part.
       * @param part the part containing the Java Activiation Framework data source.
       * @return The Java activiation data handler.
       */
  
      public static DataHandler getActiviationDataHandler(Part part) throws AxisFault{
         if(!( part instanceof AttachmentPart)){
          throw new AxisFault( "Unsupported attachment type \"" +
            part.getClass().getName()
           + "\" only supporting \"" + AttachmentPart.class.getName() +"\".");
         }
         return ((AttachmentPart) part).getActiviationDataHandler();
      }
  }
  
  
  
  1.1                  xml-axis/java/src/org/apache/axis/attachments/BoundaryDelimitedStream.java
  
  Index: BoundaryDelimitedStream.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 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.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  package org.apache.axis.attachments;
  
  /**
   * @author Rick Rineholt 
   */
  
   /**
    * This class takes the input stream and turns it multiple streams. 
    */
  public class BoundaryDelimitedStream extends java.io.FilterInputStream {
      protected byte[] boundary = null;
      int boundaryLen = 0;  //The boundary length.
      int boundaryBufLen = 0; //The boundary length plus crlf. 
      java.io.InputStream is = null; //The source input stream.
      boolean closed = true; //The stream has been closed.
      boolean eos = false;  //eof has been detected.
      boolean theEnd = false; //There are no more streams left.
  
      int readbufsz = 0; //Minimum to read at one time.
      byte[] readbuf = null; //The buffer we are reading.
      int readBufPos = 0;  //Where we have read so far in the stream.
      int readBufEnd = 0;  //The number of bytes in array.
      protected static final int BOUNDARY_NOT_FOUND = Integer.MAX_VALUE;
                                     // Where in the stream a boundary is located.
      int boundaryPos = BOUNDARY_NOT_FOUND;
  
      /**
       * Gets the next stream. From the previous using the same buffer size to read.
       * @return the boundary delmited stream. Null if there are no more streams.
       */
      public synchronized BoundaryDelimitedStream getNextStream() {
          return getNextStream(readbufsz);
      }
  
      /**
       * Gets the next stream. From the previous using  new buffer reading size.
       * @return the boundary delmited stream. Null if there are no more streams.
       */
      protected synchronized BoundaryDelimitedStream getNextStream(int readbufsz) {
          BoundaryDelimitedStream ret = null;
  
          if ( !theEnd ) {
            //Create an new boundary stream  that comes after this one.
              ret = new BoundaryDelimitedStream(this, readbufsz);
          }
          return ret;
      }
      /**
        * Constructor to create the next stream from the previous one.
        */
      protected BoundaryDelimitedStream(BoundaryDelimitedStream prev,
        int readbufsz ) {
          super (prev.is);
          boundary = prev.boundary;
          boundaryLen = prev.boundaryLen;
          boundaryBufLen = prev.boundaryBufLen;
          skip= prev.skip;
          is = prev.is;
          closed = false; //The new one is not closed.
          eos = false;  //Its not at th EOS.
          readbufsz = prev.readbufsz;
          readbuf = prev.readbuf;
          //Move past the old boundary.
          readBufPos = prev.readBufPos + boundaryBufLen; 
          readBufEnd = prev.readBufEnd;
          //find the new boundary.
          boundaryPos = boundaryPosition( readbuf, readBufPos, readBufEnd);
          prev.theEnd = theEnd; //The stream.
      }
  
      /**
       * Create a new boundary stream;
       * @param boundary is the boundary that separates the individual streams.
       * @param readbufsz lets you have some control over the amount of buffering.
       *   by buffering you can some effiency in searching.
       */
       BoundaryDelimitedStream( java.io.InputStream is, byte[] boundary,
        int readbufsz) throws org.apache.axis.AxisFault {
          super (is);
          closed = false;
          this.is = is;
          this.boundary = boundary;
          this.boundaryLen = boundary.length;
          this.boundaryBufLen = boundaryLen + 2;
            //allways leave room for at least a 2x boundary
            //Most mime boundaries are 40 bytes or so.
          this.readbufsz = Math.max( (boundaryBufLen) * 2, readbufsz); 
                                                                      
  
      }
       
      /**
       * 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 java.io.IOException {
          if (closed) throw new java.io.IOException("Stream closed.");
          if (eos) return -1;
  
          if (readbuf == null) { //Allocate the buffer.
              readbuf = new byte[Math.max(len, readbufsz ) ];
              readBufEnd = is.read(readbuf);
              readBufPos = 0;
                                                         //Finds the boundary pos.
              boundaryPos = boundaryPosition( readbuf, 0, readBufEnd);
          }
          int bwritten = 0; //Number of bytes written.
  
           //read and copy bytes in.
          do {    //Always allow to have a boundary length left in the buffer.
              int bcopy = Math.min(readBufEnd - readBufPos - boundaryBufLen,
                len - bwritten);
                                                     //never go past the boundary.
              bcopy = Math.min(bcopy, boundaryPos - readBufPos); 
  
              if (bcopy > 0) {
                  System.arraycopy(readbuf, readBufPos, b, off + bwritten, bcopy);
                  bwritten += bcopy;
                  readBufPos += bcopy;
              }
              if (readBufPos == boundaryPos) {
                  eos = true; //hit the boundary so it the end of the stream.
              }
              else if ( bwritten < len) { //need to get more data.
                  byte[]dstbuf = readbuf;
  
                  if ( readbuf.length < len) dstbuf = new byte[len];
                  int movecnt = readBufEnd - readBufPos;
  
                  //copy what was left over.
                  System.arraycopy(readbuf, readBufPos, dstbuf, 0, movecnt);
                  //Read in the new data.
                  int readcnt = is.read(dstbuf, movecnt, dstbuf.length - movecnt);
  
                  readBufEnd = readcnt + movecnt;
                  readbuf = dstbuf;
                  readBufPos = 0; //start at the begining.
                                        //just move the boundary by what we moved
                  if (BOUNDARY_NOT_FOUND != boundaryPos ) boundaryPos -= movecnt;
                  else boundaryPos = boundaryPosition( readbuf, readBufPos,
                                   readBufEnd); //See if the boundary is now there.
              }
  
          }
          //read till we get the amount or the stream is finished.
          while ( !eos && bwritten < len );
  
          if (false) {
              if (bwritten  > 0) {
                  byte tb[] = new byte[bwritten];
  
                  System.arraycopy(b, off, tb, 0, bwritten);
                  System.err.println("read(" + bwritten + ") \"" + 
                  new String(tb) + "\"");
                  System.err.flush();
              }
          }
          return bwritten;
      }
  
      /**
       * Read from the boundary 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 java.io.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 java.io.IOException {
          byte[] b = new byte[1];  //quick and dirty. //for now
          int read = read(b);
  
          if ( read < 0 ) return -1;
          else return b[0];
      }
  
      /**
       * Closes the stream.
       */
      public synchronized void close() throws java.io.IOException {
          if (closed) return;
          closed = true; //mark it closed.
          if (!eos) { //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);
              }
              while ( bread > -1 );
  
          }
      }
  
      /**
       * Read from the boundary delimited stream.
       * @return The position of the boundary. Detects the end of the source stream.
       * 
       */
      int boundaryPosition( byte[] searchbuf, int start, int end) {
  
          int foundAt = boundarySearch(searchbuf, start, end);
                                                 //First find the boundary marker
  
          if (BOUNDARY_NOT_FOUND != foundAt) { //Something was found.
  
              //If the marker has a "--" at the end then this is the last boundary.
              if ( searchbuf[foundAt + boundaryLen] == '-' && 
                                  searchbuf[foundAt + boundaryLen + 1 ] == '-' ) {
                  theEnd = true;
              }
              else if ( searchbuf[foundAt + boundaryLen] != 13 ||
                                    searchbuf[foundAt + boundaryLen + 1 ] != 10 ) {
             //If there really was no crlf at then end then this is not a boundary.
                  foundAt = BOUNDARY_NOT_FOUND;
              }
          }
          return foundAt;
      }
  
    /* The below uses a standard textbook Boyer-Moore pattern search.*/
     
  
     private int[] skip= null;
     private int boundarySearch(byte[]text,int start, int end ) {
  
         int i, j, k;
         int n= end-start;
        
         if(null == skip){
             skip= new int[256];
             java.util.Arrays.fill(skip, boundaryLen);   
             for( k=start; k<boundaryLen-1; k++ ) skip[boundary[k]] = boundaryLen-k-1;
         }
  
  
         for( k=boundaryLen-1; k < n; k += skip[text[k] & (skip.length-1)] ) {
              for( j=boundaryLen-1, i=k; j>=0 && text[i] == boundary[j]; j-- ) i--;
              if( j == (-1) ) return start+i+1;
         }
  
         return BOUNDARY_NOT_FOUND;
     }
  
  
  
  }
  
  
  
  1.1                  xml-axis/java/src/org/apache/axis/attachments/ManagedMemoryDataSource.java
  
  Index: ManagedMemoryDataSource.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 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.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  package org.apache.axis.attachments;
  
  
  /**
   * @author Rick Rineholt 
   */
  
  /**
   * This class allows small attachments to be cached in memory, while large ones are
   * cached out.  It implements a Java Activiation Data source interface.
   * TODO TODO TODO need to delete cached out data sources after a service ends.
   */
  
  public class ManagedMemoryDataSource implements  javax.activation.DataSource {
      protected String contentType = "application/octet-stream"; //Is the default.
      java.io.InputStream ss = null; //The incoming source stream.
      public static final  int MAX_MEMORY_DISK_CACHED = -1;
      protected int maxCached = 16  * 1024;  //max in memory cached. Default.
                                          //If set the file the disk is cached to.
      protected java.io.File diskCacheFile = null; 
      
                               //Memory is allocated in these size chunks.
      public static final int READ_CHUNK_SZ = 1024 ;
  
      //Should not be called; 
      protected ManagedMemoryDataSource () {
      }
  
      /**
       * Create a new boundary stream;
       * @param ss is the source input stream that is used to create this data source.. 
       * @param readbufsz lets you have some control over the amount of buffering.
       * @param maxCached  This is the max memory that is to be used to cache the data.
       * @param contentType the mime type for this data stream.
       *   by buffering you can some effiency in searching.
       */
      public ManagedMemoryDataSource(java.io.InputStream ss, int maxCached,
                                 String contentType) throws java.io.IOException {
          this (ss, maxCached, contentType, false);
      }
  
      /**
       * Create a new boundary stream;
       * @param ss is the source input stream that is used to create this data source.. 
       * @param readbufsz lets you have some control over the amount of buffering.
       * @param maxCached  This is the max memory that is to be used to cache the data.
       * @param contentType the mime type for this data stream.
       *   by buffering you can some effiency in searching.
       * @param readall if true will read in the whole source.
       */
      public ManagedMemoryDataSource(java.io.InputStream ss, int maxCached,
                String contentType,  boolean readall) throws java.io.IOException {
          this.ss = ss;
          this.maxCached = maxCached;
          if (null != contentType && contentType.length() != 0 ) this.contentType = contentType;
          if ( maxCached < MAX_MEMORY_DISK_CACHED)
              throw  new IllegalArgumentException(" maxcached value is bad: " +
                                                                     maxCached);
          //for now read all in to disk.
          if ( readall) {
              byte[] readbuffer = new byte[READ_CHUNK_SZ];
              int read = 0;
  
              do {
                  read = ss.read(readbuffer);
                  if (read > 0) write(readbuffer, read);
              }
              while ( read > -1);
              close();
          }
      }
  
      /*  javax.activation.Interface DataSource implementation */
  
      /**
       * This method returns the MIME type of the data in the form of a string. 
       * @return The mime type.
       */
      public java.lang.String getContentType() {
          return contentType;
      }
  
      /**
       *This method returns an InputStream representing the the data and throws the appropriate exception if it can not do so. 
       *@return the java.io.InputStream for the data source.
       */
      public synchronized java.io.InputStream getInputStream()
          throws java.io.IOException {
          if (memorybuflist == null) {
              return  new java.io.FileInputStream(diskCacheFile);
          }
          else
              return new Instream(); //Return the memory held stream.
      }
  
      /**
       * Return the name of the file of the stream.
       *    Optional, This is not implemented.
       */
      public java.lang.String getName() {
          return ""; //Don't really need this.
      }
  
      /** 
       *This method returns an OutputStream where the data can be written and throws the appropriate exception if it can not do so. 
       * NOT SUPPORTED, not need for axis, data sources are create by constructors.
       *
       */
      public java.io.OutputStream getOutputStream() throws java.io.IOException {
          return null;
      }
  
      protected java.util.LinkedList memorybuflist = new java.util.LinkedList(); //The linked list to hold the in memory buffers.
      protected byte[] currentMemoryBuf = null; //Hold the last memory buffer.
      protected int currentMemoryBufSz = 0; //The number of bytes written to the above buffer.
      protected int totalsz = 0;  //The total size in bytes in this data source. 
      protected java.io.FileOutputStream cachediskstream = null; //This is the cached disk stream.
                                //If true the source input stream is now closed. 
      protected boolean closed = false; 
  
      /**
       * Write bytes to the stream.
       * @param data all bytes of this array are written to the stream.
       */
      protected void write( byte[] data) throws java.io.IOException {
          write(data, data.length);
      }
  
      /**
       * This method is a low level write.
       * Note it is designed to in the future to allow streaming to both memory
       *  AND to disk simultaneously.
       */
  
      protected synchronized  void  write( byte[] data, int length)
        throws java.io.IOException {
          if (closed) throw new java.io.IOException("Stream closed stream.");
          int writesz = length;
          int byteswritten = 0;
  
          if ( null != memorybuflist && totalsz + writesz > maxCached ) { //Cache to disk.
              if (null == cachediskstream) { //Need to create a disk cache
                  diskCacheFile = java.io.File.createTempFile("Axis", "axis"); //Create a temporary file. TODO allow location to be configurable.
                  diskCacheFile.deleteOnExit(); //Insurance it goes.
                  cachediskstream = new java.io.FileOutputStream(diskCacheFile);
                  int listsz = memorybuflist.size();
                
                  //Write out the entire memory held store to disk.
                  for (java.util.Iterator it = memorybuflist.iterator();
                         it.hasNext(); ) {
                      byte[] rbuf = (byte[]) it.next();
                      int bwrite = listsz-- == 0 ? currentMemoryBufSz :
                         rbuf.length;
  
                      cachediskstream.write(rbuf, 0, bwrite);
                  }
              }
              memorybuflist = null; //bye bye to memory store.
          }
  
          if ( memorybuflist != null) { //Can write to memory.
              do {
                  if ( null == currentMemoryBuf) {
                      currentMemoryBuf = new byte[READ_CHUNK_SZ];
                      currentMemoryBufSz = 0;
                      memorybuflist.add(currentMemoryBuf);
                  }
                  //bytes to write is the min. between the remaining bytes and what is left in this buffer.
                  int bytes2write = Math.min( (writesz - byteswritten),
                            (currentMemoryBuf.length - currentMemoryBufSz));
  
                  //copy the data.
                  System.arraycopy(data, byteswritten, currentMemoryBuf,
                     currentMemoryBufSz, bytes2write);
  
                  byteswritten += bytes2write;
                  currentMemoryBufSz += bytes2write;
  
                  if (byteswritten < writesz) { //only get more if we really need it.
                      currentMemoryBuf = new byte[READ_CHUNK_SZ];
                      currentMemoryBufSz = 0;
                      memorybuflist.add(currentMemoryBuf); //add it to the chain.
                  }
              } 
              while (byteswritten < writesz);
          }
  
          if (null != cachediskstream) { //Write to the out going stream.
              cachediskstream.write(data, 0, length);
          }
  
          totalsz += writesz;
          return;
      }
  
      /**
       * This method is a low level write.
       * Close the stream. 
       */
      protected synchronized  void close() throws java.io.IOException {
          if (!closed) {
              closed = true; //Markit as closed.
              if (null != cachediskstream) { //close the disk cache.
                  cachediskstream.close();
                  cachediskstream = null;
              }
              if (null != memorybuflist) {  //There is a memory buffer.
  
                  if (currentMemoryBufSz > 0){
                      byte[] tmp = new byte[currentMemoryBufSz]; //Get the last buffer and make it the sizeof the actual data.
                      System.arraycopy(currentMemoryBuf, 0, tmp, 0,
                         currentMemoryBufSz);
                      memorybuflist.set( memorybuflist.size() - 1, tmp);  //Now replace the last buffer with this size.
                   }else{
                      memorybuflist.remove( memorybuflist.size() - 1);  //Now replace the last buffer with this size.
                   }
  
                  currentMemoryBuf = null; //No need for this anymore.
              }
          }
      }
  
      /** Inner class to handle getting an input stream to this data source
       *  Handles creating an input stream to the source.
       */
      private class Instream extends java.io.InputStream {
          protected int bread = 0; //bytes read
          java.io.FileInputStream fin = null;  //The real stream.
          int currentIndex = 0;  //The position in the list were we are reading from.
          byte[] currentBuf = null; //the buffer we are currently reading from.
          int currentBufPos = 0; //The current position in there.
  
          public int available() throws java.io.IOException {
              return totalsz - bread;
          }
  
          /**
           * Read a byte from the stream.
           * @param byte to read or -1 if no more data.
           */
          public int read() throws java.io.IOException {
              synchronized (ManagedMemoryDataSource.this){
                byte[]retb = new byte[1];
                int br = read(retb, 0, 1);
  
                if (br == -1) return -1;
                return retb[0];
              }
          }
          /**
           * Not supported.
           */
          public boolean markSupported() {
              return false;
          }
  
          /**
           * Skip bytes in the stream.
           * @param the number of bytes to skip.
           */
  
          public long skip(long skipped) throws java.io.IOException {
              synchronized (ManagedMemoryDataSource.this){
                  if ( skipped < 1) return 0; //nothing to skip.
  
                  skipped = Math.min(skipped, totalsz - bread);//only skip what we've read. 
                  if(skipped == 0) return 0;
                  java.util.List ml = memorybuflist; //hold the memory list.
                  int bwritten = 0;
  
                  if ( ml != null) {
                      if ( null == currentBuf ) { //get the buffer we need to read from.
                          currentBuf = (byte[]) ml.get(currentIndex);
                          currentBufPos = 0; //start reading from the begining.
                      }
                      do {
                          long bcopy = Math.min(currentBuf.length - currentBufPos,
                          skipped - bwritten);
  
                          bwritten += bcopy;
                          currentBufPos += bcopy;
                          if (bwritten < skipped) {
                              currentBuf = (byte[]) ml.get(++currentIndex);
                              currentBufPos = 0;
                          }
                      }
                      while ( bwritten < skipped);
                  }
                  if (null != fin) fin.skip(skipped);
                  bread += skipped;
                  return skipped;
              }
          }
  
          /**
           * Read from the stream. 
           * @param b the data buffer to write to. 
           * @param off the offset in the buffer to write to
           * @param len the number of bytes to write to the buffer.
           */
  
          public int read(byte[] b, int off, int len) throws java.io.IOException {
              if (b == null) throw new NullPointerException(
                   "input buffer is null");
              if (off < 0)  throw new IndexOutOfBoundsException
                  ("Offset is negative: " + off);
              if (len < 0)  throw new IndexOutOfBoundsException("Length: " + len);
              if (len + off > b.length) throw new IndexOutOfBoundsException(
                       "Write beyond buffer");
              if (len == 0) return 0;
  
              synchronized (ManagedMemoryDataSource.this){
                  if (closed && bread == totalsz) return -1;
                  len = Math.min(len, totalsz - bread); //Only return the number of bytes in the data store that is left.
                  java.util.List ml = memorybuflist;
  
                  int bwritten = 0;
  
                  if ( ml != null) {
                      if ( null == currentBuf ) { //Get the buffer we need to read from.
                          currentBuf = (byte[]) ml.get(currentIndex);
                          currentBufPos = 0; //New buffer start from the begining.
                      }
                      do {
                          //The bytes to copy, the minimum of the bytes left in this buffer or bytes remaining. 
                          int bcopy = Math.min(currentBuf.length - currentBufPos, len - bwritten);
  
                          //Copy the data.
                          System.arraycopy(currentBuf, currentBufPos , b,
                              off + bwritten , bcopy );
                          bwritten += bcopy;
                          currentBufPos += bcopy;
  
                          if (bwritten < len) { //Get the next buffer.
                              currentBuf = (byte[]) ml.get(++currentIndex);
                              currentBufPos = 0;
                          }
                      }
                      while ( bwritten < len);
                  }
  
                  if (bwritten == 0 && null != diskCacheFile) {
                      if (null != fin ) { //we are no reading from disk.
                          fin = new java.io.FileInputStream( diskCacheFile);
                          fin.skip(bread); //Skip what we've read so far.
                      }
                      bwritten = fin.read(b, len, off);
                  }
                  if ( bwritten > 0) bread += bwritten;
                  return bwritten;
              }
          }
  
      }//endof innerclass Instream 
  
  
      //Used to test.
      public static void main( String arg[]) { //test
          try {
              String readFile = arg[0];
              String writeFile = arg[1];
              java.io.FileInputStream ss = new java.io.FileInputStream(readFile);
  
              ManagedMemoryDataSource ms = new ManagedMemoryDataSource( ss,
               1024 * 1024,  "foo/data",  true);
              javax.activation.DataHandler dh = new javax.activation.DataHandler
                        (ms);
              java.io.InputStream is = dh.getInputStream();
              java.io.FileOutputStream fo =
                   new java.io.FileOutputStream(writeFile);
              byte[] buf = new byte[512];
              int read = 0;
  
              do {
                  read = is.read(buf);
                  if (read > 0) fo.write(buf, 0, read);
  
              }
              while (read > -1);
              fo.close();
              is.close();
  
          }
          catch ( java.lang.Exception e) {
              System.err.println(e);
              e.printStackTrace();
          }
      }
  }
  
  
  
  1.1                  xml-axis/java/src/org/apache/axis/attachments/MimeUtils.java
  
  Index: MimeUtils.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 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.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  /**
   * @author Rick Rineholt 
   * @author Wouter Cloetens (wouter@mind.be)
   */
  
  package org.apache.axis.attachments;
  
  
  import java.util.Properties;
  import javax.mail.*;
  import javax.mail.internet.*;
  import javax.activation.*;
  
  /**
   * This class is defines utilities for mime.
   */
  
  public class MimeUtils {
  
      /**
      * Determine as efficiently as possible the content length for attachments in a mail Multipart.
      * @param mp is the multipart to be serarched.
      * @return the actual length.
      */
      public static long getContentLength(javax.mail.Multipart mp ) throws javax.mail.MessagingException, java.io.IOException {
  
          int totalParts = mp.getCount();
          long totalContentLength = 0;
  
          for (int i = 0; i < totalParts; ++i) {
              javax.mail.internet.MimeBodyPart bp = (javax.mail.internet.MimeBodyPart) mp.getBodyPart(i);
  
              totalContentLength += getContentLength(bp);
          }
  
          String ctype = mp.getContentType();
          javax.mail.internet.ContentType ct = new javax.mail.internet.ContentType( ctype);
          String boundaryStr = ct.getParameter("boundary");
          int boundaryStrLen = boundaryStr.length() + 4; //must add two for -- prefix and another two for crlf
  
          return totalContentLength
              + boundaryStrLen * (totalParts + 1) //there is one more boundary than parts
              + 2 * totalParts +  //each parts data must have crlf after it.
              +2;  // last boundary has an additional --
      }
  
      /**
      * Determine the length for the individual part. 
      * @param mp is the part to be serarched.
      * @return the length in bytes.
      */
      protected  static long  getContentLength(javax.mail.internet.MimeBodyPart bp) {
          long headerLength = -1L;
          long dataSize = -1L;
  
          try {
              headerLength = getHeaderLength( bp);
              javax.activation.DataHandler dh =  bp.getDataHandler();
              javax.activation.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( "File for dataHandler does not exist" + df.getAbsolutePath());
                  dataSize = df.length();
              }
              else {
                  dataSize = bp.getSize();
                  if (-1 == dataSize ) { //Data size is not known so read it the hard way...
                      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) {
              System.err.println(e);
          }
          return dataSize + headerLength;
      }
      /**
       * Gets the header length for any part.
       * @param the part to determine the header length for.
       * @return the length in bytes.
       */
      private static long  getHeaderLength(javax.mail.internet.MimeBodyPart bp) throws javax.mail.MessagingException, java.io.IOException {
  
          javax.mail.internet.MimeBodyPart headersOnly = new javax.mail.internet.MimeBodyPart(new javax.mail.internet.InternetHeaders(), new byte[0]);
  
          for ( java.util.Enumeration en = bp.getAllHeaders(); en.hasMoreElements() ; ) {
              javax.mail.Header header = (javax.mail.Header) en.nextElement();
  
              headersOnly.addHeader( header.getName(), header.getValue());
          }
  
          java.io.ByteArrayOutputStream bas = new java.io.ByteArrayOutputStream( 1024 * 16);
  
          headersOnly.writeTo(bas);
          bas.close();
  
          return (long) bas.size(); //This has header length plus the crlf part that seperates the data
      }
  
      public static String[] filter = new String[] { "Message-ID", "Mime-Version", "Content-Type" } ;
      
      /**
       * This routine will the multi part type and write it out to a stream.
       * @param os is the output stream to write to.
       * @param the multipart that needs to be written to the stream.
       */
      public static void writeToMultiPartStream(java.io.OutputStream os, javax.mail.internet.MimeMultipart  mp) {
          try {
              java.util.Properties props = System.getProperties();
  
              props.put("mail.smtp.host", "localhost"); //this is a bogus since we will never mail it.
              javax.mail.Session session = javax.mail.Session.getInstance(props, null);
              javax.mail.internet.MimeMessage message = new javax.mail.internet.MimeMessage(session);
  
              message.setContent(mp);
              message.saveChanges();
              message.writeTo(os, filter);
  
          }
          catch (javax.mail.MessagingException e) {
              System.err.println(e);
              e.printStackTrace();
          }
          catch (java.io.IOException e) {
              System.err.println(e);
              e.printStackTrace();
          }
      }
      /**
       * This routine will get the content type.
       */
      public static String getContentType(javax.mail.internet.MimeMultipart mp) {
          return  mp.getContentType();
      }
      /**
       * This routine will create a multipar object from the parts and the SOAP content.
       * @param the env should be the text for the main root part.
       * @param the parts contain a collection of  mappings of cids to the message parts.
       */
  
      public static javax.mail.internet.MimeMultipart createMP(String env, java.util.Map parts ) throws org.apache.axis.AxisFault {
          javax.mail.internet.MimeMultipart multipart = null;
  
          try {
              String rootCID = getNewContentIdValue();
  
              if (rootCID.startsWith("cid:")) rootCID = rootCID.substring(4);
              multipart = new javax.mail.internet.MimeMultipart("related; start=\"<" + rootCID + ">\"" );
  
              javax.mail.internet.MimeBodyPart messageBodyPart = new javax.mail.internet.MimeBodyPart();
  
              messageBodyPart.setText(env);
              messageBodyPart.setHeader("Content-Type", "text/xml; charset=utf-8" );
              messageBodyPart.setHeader("Content-ID", "<" + rootCID  + ">" );
              messageBodyPart.setHeader("Content-Transfer-Encoding", "8bit");
  
              multipart.addBodyPart(messageBodyPart);
              java.util.Set pe = parts.entrySet();
  
              for (java.util.Iterator it = pe.iterator(); it.hasNext(); ) {
                  java.util.Map.Entry es = (java.util.Map.Entry) it.next();
                  javax.activation.DataHandler dh =
                      org.apache.axis.attachments.AttachmentUtils.getActiviationDataHandler((org.apache.axis.Part) es.getValue());
                  String contentID = (String) es.getKey();
  
                  if (contentID.startsWith("cid:")) contentID = contentID.substring(4);
  
                  messageBodyPart = new javax.mail.internet.MimeBodyPart();
  
                  messageBodyPart.setDataHandler(dh);
                  String contentType = dh.getContentType();
  
                  if (contentType == null || contentType.trim().length() == 0) {
                      contentType = "application/octet-stream";
                  }
                  messageBodyPart.setHeader("Content-Type", contentType );
                  messageBodyPart.setHeader("Content-ID", "<" + contentID  + ">" );
                  messageBodyPart.setHeader("Content-Transfer-Encoding", "binary"); //Safe and fastest for anything other than mail;
                  multipart.addBodyPart(messageBodyPart);
              }
          }
          catch (javax.mail.MessagingException e) {
              System.err.println(e);
              e.printStackTrace();
          }
          return multipart ;
      }
  
      static String thisHost = null;
  
      private static int count = (int) (Math.random() * 100);
  
      public static String getNewContentIdValue() {
          int lcount;
  
          synchronized (org.apache.axis.Message.class  ) {
              lcount = ++count;
          }
          if (null == thisHost) {
              try {
                  thisHost = java.net.InetAddress.getLocalHost().getHostName();
              } 
              catch (java.net.UnknownHostException e) {
                  System.err.println("exception:" + e);
                  thisHost = "localhost";
                  e.printStackTrace();
              }
          }
  
          StringBuffer s = new StringBuffer();
  
          // Unique string is <hashcode>.<currentTime>.apache-soap.<hostname>
          s.append("cid:").append( lcount).append(s.hashCode()).append('.').append(System.currentTimeMillis()).append(".AXIS@").append(thisHost);
          return s.toString();
      }
  }
  
  
  
  1.1                  xml-axis/java/src/org/apache/axis/attachments/MultiPartRelatedInputStream.java
  
  Index: MultiPartRelatedInputStream.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 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.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   */
  
  package org.apache.axis.attachments;
  
  
  /**
   *
   * @author Rick Rineholt 
   */
  
   /** This simulates the multipart stream 
    *
    */
  public class MultiPartRelatedInputStream extends java.io.FilterInputStream {
      public static final String MIME_MULTIPART_RELATED = "multipart/related";
      protected java.util.HashMap parts =  new java.util.HashMap();
      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.
      protected java.io.InputStream is = null; //The orginal multipart/related stream.
      //This stream controls and manages the  boundary.
      protected org.apache.axis.attachments.BoundaryDelimitedStream boundaryDelimitedStream = 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.
  
      /**
       * Multipart stream.
       * @param the string that holds the contentType
       * @param is the true input stream from where the source.
       */
      public MultiPartRelatedInputStream ( String contentType, java.io.InputStream is) throws org.apache.axis.AxisFault {
          super (is);
          try {
              this.is = is;
              //First find the start and boundary parameters. There are real weird rules regard what
              // can be in real headers what needs to be escaped etc  let mail parse it.
              javax.mail.internet.ContentType ct = new javax.mail.internet.ContentType(contentType);
  
              String boundaryStr = "--" + ct.getParameter("boundary"); //The boundary with -- add as always the case.
  
              String rootPartContentId = ct.getParameter("start"); //Get the root part content.
  
              if (rootPartContentId != null) {
                  rootPartContentId = rootPartContentId.trim();
                  if (rootPartContentId.startsWith("<")) rootPartContentId = rootPartContentId.substring(1);
                  if (rootPartContentId.endsWith(">")) rootPartContentId = rootPartContentId.substring(0, rootPartContentId.length() - 1);
                  if (!rootPartContentId.startsWith("cid:")) rootPartContentId = "cid:" + rootPartContentId;
              }
  
              //if start is null then the first attachment is the rootpart
              //First read the start boundary -- this is done with brute force since the servlet may swallow the crlf between headers.
              // after this we use the more efficient boundarydelimeted stream.  There should never be any data here anyway.
              byte[][] boundaryMarker = new byte[2][boundaryStr.length() + 2];
  
              is.read(boundaryMarker[0]);
  
              boundary = (boundaryStr + "\r\n").getBytes("US-ASCII");
              int current = 0;
              //This just goes brute force one byte at a time to find the first boundary.  
              // in most cases this just a crlf.
              for (boolean found = false; !found; ++current) {
                  if (!(found = java.util.Arrays.equals(boundaryMarker[current & 0x1], boundary))) {
                      System.arraycopy(boundaryMarker[current & 0x1], 1, boundaryMarker[(current + 1) & 0x1], 0, boundaryMarker[0].length - 1);
                      if ( is.read(boundaryMarker[(current + 1) & 0x1], boundaryMarker[0].length - 1, 1) < 1) {
                          throw new org.apache.axis.AxisFault( "Error in MIME data stream start boundary not found expected:\"" +
                                  new String(boundary) );
                      }
                  }
              }
  
              //after the first boundary each boundary will have a cr lf at the beginning since after the data in any part there
              // is a cr lf added to put the boundary at the begining of a line.
              boundaryStr = "\r\n"  + boundaryStr;
              boundary = boundaryStr.getBytes("US-ASCII");
  
              //create the boundary delmited stream.
              boundaryDelimitedStream = new org.apache.axis.attachments.BoundaryDelimitedStream( is, boundary, 1024);
              String contentId = null;
  
              //Now read through all potential streams until we have found the root part.
              do {
                  contentId = null;
                  String contentTransferEncoding = null;
                  String contentLocation = null;
                  //Read this attachments headers from the stream.  
                  javax.mail.internet.InternetHeaders headers = new javax.mail.internet.InternetHeaders(boundaryDelimitedStream);
                  //Use java mail utility to read through the headers.
                  contentId = headers.getHeader("Content-ID", null);
                  //Clean up the headers and remove any < >
                  if (contentId != null) {
                      contentId = contentId.trim();
                      if (contentId.startsWith("<")) contentId = contentId.substring(1);
                      if (contentId.endsWith(">")) contentId = contentId.substring(0, contentId.length() - 1);
                      if (!contentId.startsWith("cid:")) contentId = "cid:" + contentId; //make sure its identified as cid
                  }
                  contentType = headers.getHeader("Content-Type", null);
                  if (contentType != null) contentType = contentType.trim();
                  contentLocation = headers.getHeader("Content-Location", null);
                  if (contentLocation != null) contentLocation = contentLocation.trim();
                  contentTransferEncoding = headers.getHeader("Content-Transfer-Encoding", null);
                  if (contentTransferEncoding != null ) contentTransferEncoding = contentTransferEncoding.trim();
                  //TODO still need to add support for bas64 and quoted printable.
  
                  if (rootPartContentId != null && !rootPartContentId.equals( contentId)) { //This is a part that has come in prior to the root part. Need to buffer it up.
                      javax.activation.DataHandler dh = new javax.activation.DataHandler(new org.apache.axis.attachments.ManagedMemoryDataSource(boundaryDelimitedStream, 16 * 1024, contentType, true));
  
                      addPart(contentId, contentLocation, dh);
                      boundaryDelimitedStream = boundaryDelimitedStream.getNextStream(); //Gets the next stream.
                  }
  
              }
              while ( null != boundaryDelimitedStream &&  rootPartContentId != null && !rootPartContentId.equals( contentId) );
  
              if (boundaryDelimitedStream  == null ) {
                  throw new org.apache.axis.AxisFault( "Root part containing SOAP envelope not found.  contentId=" + rootPartContentId);
              }
              soapStream = boundaryDelimitedStream; //This should be the SOAP part
  
          //Read from the input stream all attachments prior to the root part.
          }
          catch (javax.mail.internet.ParseException e) {
              throw new org.apache.axis.AxisFault( "Error in parsing mime data stream " + e.getMessage());
          }
          catch ( java.io.IOException e) {
              throw new org.apache.axis.AxisFault( "Error in reading data stream " + e.getMessage());
          }
          catch ( javax.mail.MessagingException e) {
              throw new org.apache.axis.AxisFault( "Error in reading data stream " + e.getMessage());
          }
      }
  
      public javax.activation.DataHandler getAttachmentByReference( String id ) throws org.apache.axis.AxisFault {  // if CID should still have CID: prefix.  
          //First see if we have read it in yet.
          javax.activation.DataHandler ret = (javax.activation.DataHandler) parts.get(id);
  
          if ( null == ret) {
              ret = readTillFound(id);
          }
          return ret;
      }
  
      protected void addPart(String contentId, String locationId, javax.activation.DataHandler dh) {
          if (contentId != null && contentId.trim().length() != 0) parts.put(contentId, dh);
          if (locationId != null && locationId.trim().length() != 0)parts.put(locationId, dh);
      }
  
      /** 
       * 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 javax.activation.DataHandler readTillFound( final String id) throws org.apache.axis.AxisFault {
          if (boundaryDelimitedStream == null) return null; //The whole stream has been consumed already
          javax.activation.DataHandler ret = null;
  
          try {
              if ( soapStream == boundaryDelimitedStream ) { //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 = boundaryDelimitedStream.read(buf);
                          if (byteread > 0) soapdata.write(buf, 0, byteread);
                      }
                      while (byteread > -1);
                      soapdata.close();
                      soapStream = new java.io.ByteArrayInputStream( soapdata.toByteArray());
                  }
                  boundaryDelimitedStream = boundaryDelimitedStream.getNextStream();
              }
                 //Now start searching for the data.
  
              do {
                  String contentType = null;
                  String contentId = null;
                  String contentTransferEncoding = null;
                  String contentLocation = null;
  
                  //Read this attachments headers from the stream.  
                  javax.mail.internet.InternetHeaders headers = new javax.mail.internet.InternetHeaders(boundaryDelimitedStream);
  
                  contentId = headers.getHeader("Content-ID", null);
                  if (contentId != null) {
                      contentId = contentId.trim();
                      if (contentId.startsWith("<")) contentId = contentId.substring(1);
                      if (contentId.endsWith(">")) contentId = contentId.substring(0, contentId.length() - 1);
                      if (!contentId.startsWith("cid:")) contentId = "cid:" + contentId;
                  }
                  contentType = headers.getHeader("Content-Type", null);
                  if (contentType != null) contentType = contentType.trim();
                  contentLocation = headers.getHeader("Content-Location", null);
                  if (contentLocation != null) contentLocation = contentLocation.trim();
                  contentTransferEncoding = headers.getHeader("Content-Transfer-Encoding", null);
                  if (contentTransferEncoding != null ) contentTransferEncoding = contentTransferEncoding.trim();
  
                  javax.activation.DataHandler dh = new javax.activation.DataHandler(new org.apache.axis.attachments.ManagedMemoryDataSource(boundaryDelimitedStream, 1024, contentType, true));
  
                  addPart(contentId, contentLocation, dh);
  
                  if (contentId != null && id.equals( contentId)) { //This is the part being sought
                      ret = dh;
                  }
                  boundaryDelimitedStream = boundaryDelimitedStream.getNextStream();
  
              }
              while (null == ret && null != boundaryDelimitedStream);
          }
          catch (Exception e) {
              throw new org.apache.axis.AxisFault(e);
          }
  
          return ret;
      }
  
      /**
       * Read the root stream. 
       */
  
      public int read(byte[] b, int off, int len) throws java.io.IOException {
          if (closed) throw new java.io.IOException("Stream closed.");
          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("Stream closed.");
          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.8       +1 -2      xml-axis/java/src/org/apache/axis/configuration/FileProvider.java
  
  Index: FileProvider.java
  ===================================================================
  RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/configuration/FileProvider.java,v
  retrieving revision 1.7
  retrieving revision 1.8
  diff -u -r1.7 -r1.8
  --- FileProvider.java	2001/11/06 15:57:41	1.7
  +++ FileProvider.java	2001/11/06 20:06:02	1.8
  @@ -58,7 +58,6 @@
   import org.apache.axis.AxisEngine;
   import org.apache.axis.ConfigurationProvider;
   import org.apache.axis.utils.Admin;
  -import org.apache.axis.utils.JavaUtils;
   import org.apache.axis.utils.XMLUtils;
   import org.w3c.dom.Document;
   
  @@ -112,7 +111,7 @@
           }
   
           if (is == null) {
  -            throw new Exception(JavaUtils.getMessage("noconfig00"));
  +            throw new Exception("No engine configuration file - aborting!");
           }
   
           Document doc = XMLUtils.newDocument(is);
  
  
  
  1.1                  xml-axis/java/src/org/apache/axis/handlers/MD5AttachHandler.java
  
  Index: MD5AttachHandler.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 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.<t_X>env.get
   * ====================================================================
   *
   * 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.handlers;
  
  
  import org.apache.axis.AxisFault;
  import org.apache.axis.Message;
  import org.apache.axis.MessageContext;
  import org.apache.axis.message.SOAPEnvelope;
  import org.apache.log4j.Category;
  
  
  /**
   *
   * @author Doug Davis (dug@us.ibm.com)
   * @author Rick Rineholt 
   */
  public class MD5AttachHandler extends org.apache.axis.handlers.BasicHandler {
      static Category category =
          Category.getInstance(MD5AttachHandler.class.getName());
  
      public void invoke(MessageContext msgContext) throws AxisFault {
          category.debug("Enter: EchoHandler::invoke" );
          try {
              // System.err.println("IN MD5");        
              Message  msg = msgContext.getRequestMessage();
              org.apache.axis.SOAPPart soapPart = (org.apache.axis.SOAPPart) msg.getSOAPPart();
              org.apache.axis.message.SOAPEnvelope env = (org.apache.axis.message.SOAPEnvelope) soapPart.getAsSOAPEnvelope();
              org.apache.axis.message.SOAPBodyElement sbe = env.getFirstBody();//env.getBodyByName("ns1", "addedfile");
              org.w3c.dom.Element sbElement = sbe.getAsDOM();
              //get the first level accessor  ie parameter
              org.w3c.dom.Node n = sbElement.getFirstChild();
  
              for (; n != null && !(n instanceof org.w3c.dom.Element); n = n.getNextSibling());
              org.w3c.dom.Element paramElement = (org.w3c.dom.Element) n;
              //Get the href associated with the attachment.
              String href = paramElement.getAttribute(org.apache.axis.Constants.ATTR_HREF);
              org.apache.axis.Part ap = msg.getAttachments().getAttachmentByReference(href);
              javax.activation.DataHandler dh = org.apache.axis.attachments.AttachmentUtils.getActiviationDataHandler(ap);
              org.w3c.dom.Node timeNode = paramElement.getFirstChild();
              long startTime = -1;
  
              if (timeNode != null && timeNode instanceof org.w3c.dom.Text) {
                  String startTimeStr = ((org.w3c.dom.Text) timeNode).getData();
  
                  startTime = Long.parseLong(startTimeStr);
              }
              // System.err.println("GOTIT");
  
              long receivedTime = System.currentTimeMillis();
              long elapsedTime = -1;
  
              // System.err.println(startTime);            
              // System.err.println(receivedTime);            
              if (startTime > 0) elapsedTime = receivedTime - startTime;
              String elapsedTimeStr = elapsedTime + "";
              // System.err.println(elapsedTimeStr);            
  
              java.security.MessageDigest md = java.security.MessageDigest.getInstance("MD5");
              java.io.InputStream attachmentStream =  dh.getInputStream();
              int bread = 0;
              byte[] buf = new byte[64 * 1024];
  
              do {
                  bread = attachmentStream.read(buf);
                  if (bread > 0) {
                      md.update(buf, 0, bread);
                  }
              }
              while (bread > -1);
              attachmentStream.close();
              buf = null;
              //Add the mime type to the digest.
              String contentType = dh.getContentType();
  
              if (contentType != null && contentType.length() != 0) {
                  md.update( contentType.getBytes("US-ASCII"));
              }
  
              sbe = env.getFirstBody();
              sbElement = sbe.getAsDOM();
              //get the first level accessor  ie parameter
              n = sbElement.getFirstChild();
              for (; n != null && !(n instanceof org.w3c.dom.Element); n = n.getNextSibling());
              paramElement = (org.w3c.dom.Element) n;
              // paramElement.setAttribute(org.apache.axis.Constants.ATTR_HREF, respHref);
              String MD5String = org.apache.axis.encoding.Base64.encode(md.digest());
              String senddata = " elapsedTime=" + elapsedTimeStr + " MD5=" + MD5String;
  
              // System.err.println(senddata);            
              paramElement.appendChild( paramElement.getOwnerDocument().createTextNode(senddata));
  
              sbe = new org.apache.axis.message.SOAPBodyElement(sbElement);
              env.clearBody();
              env.addBodyElement(sbe);
              msg = new Message( env );
  
              msgContext.setResponseMessage( msg );
          }
          catch ( Exception e ) {
              category.error( e );
              throw new AxisFault( e );
          }
          category.debug("Exit: EchoHandler::invoke" );
      }
  
      public void undo(MessageContext msgContext) {
          category.debug("Enter: EchoHandler::undo" );
          category.debug("Exit: EchoHandler::undo" );
      }
  
  }
  
  
  
  1.58      +9 -0      xml-axis/java/src/org/apache/axis/message/MessageElement.java
  
  Index: MessageElement.java
  ===================================================================
  RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/message/MessageElement.java,v
  retrieving revision 1.57
  retrieving revision 1.58
  diff -u -r1.57 -r1.58
  --- MessageElement.java	2001/11/02 03:07:41	1.57
  +++ MessageElement.java	2001/11/06 20:06:02	1.58
  @@ -331,6 +331,15 @@
           attributes.addAttribute(namespace, localName, "", "CDATA",
                                   value);
       }
  +    
  +    public String getAttributeValue(String localName)
  +    {
  +        if (attributes == null) {
  +           return null;
  +        }
  +        return attributes.getValue(localName);
  +    }
  +
   
       public void setEnvelope(SOAPEnvelope env)
       {
  
  
  
  1.59      +11 -12    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.58
  retrieving revision 1.59
  diff -u -r1.58 -r1.59
  --- AxisServlet.java	2001/11/02 03:07:41	1.58
  +++ AxisServlet.java	2001/11/06 20:06:02	1.59
  @@ -315,8 +315,8 @@
           /* even need to be parsed.                                         */
           /*******************************************************************/
           MessageContext    msgContext = new MessageContext(engine);
  -        InputStream       inp        = req.getInputStream();
  -        Message           msg        = new Message( inp );
  +        String contentType= req.getHeader( HTTPConstants.HEADER_CONTENT_TYPE);
  +        Message           msg        = new Message( req.getInputStream(), false, contentType );
   
           /* Set the request(incoming) message field in the context */
           /**********************************************************/
  @@ -417,16 +417,15 @@
   
           /* Send it back along the wire...  */
           /***********************************/
  -        // ROBJDO this must change for MIME outgoing content
  -        msg = msgContext.getResponseMessage();
  -        res.setContentType( "text/xml; charset=utf-8" );
  -        String response;
  -        if (msg == null) {
  -            response="No data";
  -        } else {
  -            response = (String)msg.getSOAPPart().getAsString();
  +
  +        if(null== (msg = msgContext.getResponseMessage())) {
  +          String resp= "No data";
  +          res.setContentLength( resp.getBytes().length );
  +          res.getWriter().print(resp);
  +        }else{
  +          res.setContentType( msg.getContentType() );
  +          res.setContentLength( msg.getContentLength() );
  +          msg.writeContentToStream(res.getOutputStream());
           }
  -        res.setContentLength( response.getBytes().length );
  -        res.getWriter().print( response );
       }
   }
  
  
  
  1.30      +9 -9      xml-axis/java/src/org/apache/axis/transport/http/HTTPSender.java
  
  Index: HTTPSender.java
  ===================================================================
  RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/transport/http/HTTPSender.java,v
  retrieving revision 1.29
  retrieving revision 1.30
  diff -u -r1.29 -r1.30
  --- HTTPSender.java	2001/11/02 03:07:41	1.29
  +++ HTTPSender.java	2001/11/06 20:06:02	1.30
  @@ -205,11 +205,7 @@
                   sock.setSoTimeout(msgContext.getTimeout());
               }
   
  -            // ROBJDO must be stream-oriented here
  -            reqEnv  = (String) msgContext.getRequestMessage().getSOAPPart().getAsString();
   
  -            //System.out.println("Msg: " + reqEnv);
  -
               BufferedInputStream inp = new BufferedInputStream(sock.getInputStream());
               OutputStream  out  = sock.getOutputStream();
               StringBuffer  otherHeaders = new StringBuffer();
  @@ -252,8 +248,7 @@
               }
   
               StringBuffer header = new StringBuffer();
  -            // ROBJDO envelope API needs to change???  to avoid getting all the bytes
  -            byte[] request = reqEnv.getBytes();
  +            // byte[] request = reqEnv.getBytes();
   
               header.append( HTTPConstants.HEADER_POST )
                .append(" " );
  @@ -263,17 +258,22 @@
                   header.append( ((tmpURL.getFile() == null ||
                           tmpURL.getFile().equals(""))? "/": tmpURL.getFile()) );
               }
  +
  +            Message reqMessage= msgContext.getRequestMessage();
  +
               header.append( " HTTP/1.0\r\n" )
                .append( HTTPConstants.HEADER_CONTENT_LENGTH )
                .append( ": " )
  -             .append( request.length )
  +             .append( reqMessage.getContentLength() )
                .append( "\r\n" )
                .append( HTTPConstants.HEADER_HOST )
                .append( ": " )
                .append( host )
                .append( "\r\n" )
                .append( HTTPConstants.HEADER_CONTENT_TYPE )
  -             .append( ": text/xml; charset=utf-8\r\n" )
  +             .append( ": " )
  +             .append( reqMessage.getContentType())
  +             .append( "\r\n" )
                .append( (otherHeaders == null ? "" : otherHeaders.toString()))
                .append( HTTPConstants.HEADER_SOAP_ACTION )
                .append( ": \"" )
  @@ -283,7 +283,7 @@
               header.append("\r\n");
   
               out.write( header.toString().getBytes() );
  -            out.write( request );
  +            reqMessage.writeContentToStream(out);
   
               category.debug( "XML sent:" );
               category.debug( "---------------------------------------------------");
  
  
  
  1.1                  xml-axis/java/test/md5attach/MD5AttachTest.java
  
  Index: MD5AttachTest.java
  ===================================================================
  package test.md5attach;
  
  import java.io.*;
  import org.apache.axis.*;
  import org.apache.axis.client.ServiceClient;
  import org.apache.axis.transport.http.HTTPTransport ;
  import org.apache.axis.utils.*;
  
  /**
   * A convenient little test program which will send a message as is to
   * the server.  Useful for debugging interoperability problems or 
   * handling of ill-formed messages that are hard to reproduce programmatically.
   *
   * Accepts the standard options, followed by a list of files containing
   * the contents to be sent.
   */
  public class MD5AttachTest {
      static void main(String[] args) throws Exception {
          Options opts = new Options(args);
          String action = opts.isValueSet('a');
  
          ServiceClient sc = new ServiceClient(opts.getURL());
          //if (action != null) sc.set(HTTPTransport.ACTION, action);
          sc.set(HTTPTransport.ACTION, "");
    
              args = opts.getRemainingArgs();
  
             if(null == args || args.length != 1)  {
                System.err.println("Must specify file to send as an attachment!");
                System.exit(8);
              }
  
              //Create the attachment.
              javax.activation.DataHandler dh= new javax.activation.DataHandler( new javax.activation.FileDataSource( args[0] ));
  
              org.apache.axis.message.SOAPEnvelope env= new org.apache.axis.message.SOAPEnvelope();
  
              //Build the body elements.
              javax.xml.parsers.DocumentBuilderFactory dbf= javax.xml.parsers.DocumentBuilderFactory.newInstance();
              javax.xml.parsers.DocumentBuilder db= dbf.newDocumentBuilder();
              org.w3c.dom.Document doc= db.newDocument();
              org.w3c.dom.Element methodElement=   doc.createElementNS("foo", "foo:MD5Attach");
              org.w3c.dom.Element paramElement=   doc.createElementNS("foo", "foo:thefile");
              long startTime= System.currentTimeMillis();
              methodElement.appendChild(paramElement);
              paramElement.appendChild( doc.createTextNode(""+startTime));
  
  
              org.apache.axis.message.SOAPBodyElement sbe= new org.apache.axis.message.SOAPBodyElement( methodElement);
              env.addBodyElement(sbe);
              
              org.apache.axis.Message msg=  new org.apache.axis.Message(env);
  
              //Add the attachment content to the message.
              org.apache.axis.attachments.Attachments attachments= msg.getAttachments();
              org.apache.axis.Part attachmentPart= attachments.createAttachmentPart(dh);
              String href= attachmentPart.getContentId();
              //Have the parameter element set an href attribute to the attachment.
              paramElement.setAttribute(org.apache.axis.Constants.ATTR_HREF, href );
              env.clearBody();
              env.addBodyElement(sbe);
  
              msg.getSOAPPart().setSOAPEnvelope(env);
  
              sc.setRequestMessage(msg);
              //go on now....
              sc.invoke();
          
              MessageContext mc = sc.getMessageContext();
              // System.out.println(mc.getResponseMessage().getAsString());
              
              env =  mc.getResponseMessage().getSOAPPart().getAsSOAPEnvelope();
              sbe= env.getFirstBody();
              org.w3c.dom.Element sbElement= sbe.getAsDOM();
              //get the first level accessor  ie parameter
              org.w3c.dom.Node n= sbElement.getFirstChild();
              for(; n != null && !(n instanceof org.w3c.dom.Element); n= n.getNextSibling());
              paramElement= (org.w3c.dom.Element)n; 
  
              org.w3c.dom.Node respNode= paramElement.getFirstChild();
              long elapsedTime= -1; 
              if(respNode!= null && respNode instanceof org.w3c.dom.Text){
                
                String respStr= ((org.w3c.dom.Text) respNode).getData();
                String timeStr= null;
                String MD5String= null; 
                java.util.StringTokenizer st = new java.util.StringTokenizer(respStr);
                 while (st.hasMoreTokens()) {
                     String s= st.nextToken().trim();
                     if(s.startsWith("elapsedTime=")) timeStr= s.substring(12);
                     else if(s.startsWith("MD5=")) MD5String= s.substring(4);
                 }
                 if(timeStr != null){
                   long time = Long.parseLong(timeStr);
                   timeStr= (time/100.0) + " sec.";
                 }else{
                   timeStr= "Unknown";
                 }
                 System.out.println("The time to send was:" + timeStr);
                 if(MD5String == null){
                   System.err.println("Sorry no MD5 data was received.");
                 }else{
                   System.out.println("Calculating MD5 for local file...");
                   java.security.MessageDigest md= java.security.MessageDigest.getInstance("MD5");
                   byte[] MD5received= org.apache.axis.encoding.Base64.decode(MD5String);
                    java.io.InputStream attachmentStream=  dh.getInputStream();
                    int bread=0;
                    byte[] buf= new byte[64*1024];
                    do{
                      bread= attachmentStream.read(buf);
                      if(bread >0){
                        md.update(buf, 0, bread);
                      }
                    }while(bread > -1);
                    attachmentStream.close();
                    buf= null;
                    //Add the mime type to the digest.
                    String contentType= dh.getContentType();
                    if(contentType != null && contentType.length() != 0){
                      md.update( contentType.getBytes("US-ASCII")); 
                    }
                    byte[] MD5orginal= md.digest(); 
                    if( java.util.Arrays.equals(MD5orginal, MD5received)){
                      System.out.println("All is well with Axis's attachment support!");
                      System.exit(0);
                    }else{
                      System.err.println("Miss match in MD5");
                    }
                 }
              }else{
              System.err.println("Sorry no returned data.");
              }
            System.err.println("You've found a bug:\"http://nagoya.apache.org/bugzilla/\"");
            System.exit(8);
          }
  
  }
  
  
  

Mime
View raw message