You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hc.apache.org by ol...@apache.org on 2013/06/21 14:28:59 UTC

svn commit: r1495409 - in /httpcomponents/httpclient/trunk/httpmime/src: main/java/org/apache/http/entity/mime/ test/java/org/apache/http/entity/mime/

Author: olegk
Date: Fri Jun 21 12:28:58 2013
New Revision: 1495409

URL: http://svn.apache.org/r1495409
Log:
Post HTTPCLIENT-1372 cleanups; all multipart entity generation logic moved to MultipartEntityBuilder

Added:
    httpcomponents/httpclient/trunk/httpmime/src/main/java/org/apache/http/entity/mime/MultipartFormEntity.java   (with props)
Removed:
    httpcomponents/httpclient/trunk/httpmime/src/main/java/org/apache/http/entity/mime/HttpMultipartFactory.java
Modified:
    httpcomponents/httpclient/trunk/httpmime/src/main/java/org/apache/http/entity/mime/AbstractMultipartForm.java
    httpcomponents/httpclient/trunk/httpmime/src/main/java/org/apache/http/entity/mime/HttpBrowserCompatibleMultipart.java
    httpcomponents/httpclient/trunk/httpmime/src/main/java/org/apache/http/entity/mime/HttpMultipart.java
    httpcomponents/httpclient/trunk/httpmime/src/main/java/org/apache/http/entity/mime/HttpRFC6532Multipart.java
    httpcomponents/httpclient/trunk/httpmime/src/main/java/org/apache/http/entity/mime/HttpStrictMultipart.java
    httpcomponents/httpclient/trunk/httpmime/src/main/java/org/apache/http/entity/mime/MIME.java
    httpcomponents/httpclient/trunk/httpmime/src/main/java/org/apache/http/entity/mime/MultipartEntity.java
    httpcomponents/httpclient/trunk/httpmime/src/main/java/org/apache/http/entity/mime/MultipartEntityBuilder.java
    httpcomponents/httpclient/trunk/httpmime/src/test/java/org/apache/http/entity/mime/TestMultipartEntityBuilder.java
    httpcomponents/httpclient/trunk/httpmime/src/test/java/org/apache/http/entity/mime/TestMultipartForm.java
    httpcomponents/httpclient/trunk/httpmime/src/test/java/org/apache/http/entity/mime/TestMultipartFormHttpEntity.java

Modified: httpcomponents/httpclient/trunk/httpmime/src/main/java/org/apache/http/entity/mime/AbstractMultipartForm.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpmime/src/main/java/org/apache/http/entity/mime/AbstractMultipartForm.java?rev=1495409&r1=1495408&r2=1495409&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpmime/src/main/java/org/apache/http/entity/mime/AbstractMultipartForm.java (original)
+++ httpcomponents/httpclient/trunk/httpmime/src/main/java/org/apache/http/entity/mime/AbstractMultipartForm.java Fri Jun 21 12:28:58 2013
@@ -33,7 +33,6 @@ import java.io.OutputStream;
 import java.nio.ByteBuffer;
 import java.nio.CharBuffer;
 import java.nio.charset.Charset;
-import java.util.ArrayList;
 import java.util.List;
 
 import org.apache.http.entity.mime.content.ContentBody;
@@ -94,11 +93,9 @@ abstract class AbstractMultipartForm {
     private static final ByteArrayBuffer CR_LF = encode(MIME.DEFAULT_CHARSET, "\r\n");
     private static final ByteArrayBuffer TWO_DASHES = encode(MIME.DEFAULT_CHARSET, "--");
 
-
     private final String subType;
     protected final Charset charset;
     private final String boundary;
-    private final List<FormBodyPart> parts;
 
     /**
      * Creates an instance with the specified settings.
@@ -115,7 +112,6 @@ abstract class AbstractMultipartForm {
         this.subType = subType;
         this.charset = charset != null ? charset : MIME.DEFAULT_CHARSET;
         this.boundary = boundary;
-        this.parts = new ArrayList<FormBodyPart>();
     }
 
     public AbstractMultipartForm(final String subType, final String boundary) {
@@ -130,27 +126,18 @@ abstract class AbstractMultipartForm {
         return this.charset;
     }
 
-    public List<FormBodyPart> getBodyParts() {
-        return this.parts;
-    }
-
-    public void addBodyPart(final FormBodyPart part) {
-        if (part == null) {
-            return;
-        }
-        this.parts.add(part);
-    }
+    public abstract List<FormBodyPart> getBodyParts();
 
     public String getBoundary() {
         return this.boundary;
     }
 
-    private void doWriteTo(
+    void doWriteTo(
         final OutputStream out,
         final boolean writeContent) throws IOException {
 
         final ByteArrayBuffer boundary = encode(this.charset, getBoundary());
-        for (final FormBodyPart part: this.parts) {
+        for (final FormBodyPart part: getBodyParts()) {
             writeBytes(TWO_DASHES, out);
             writeBytes(boundary, out);
             writeBytes(CR_LF, out);
@@ -201,7 +188,7 @@ abstract class AbstractMultipartForm {
      */
     public long getTotalLength() {
         long contentLen = 0;
-        for (final FormBodyPart part: this.parts) {
+        for (final FormBodyPart part: getBodyParts()) {
             final ContentBody body = part.getBody();
             final long len = body.getContentLength();
             if (len >= 0) {

Modified: httpcomponents/httpclient/trunk/httpmime/src/main/java/org/apache/http/entity/mime/HttpBrowserCompatibleMultipart.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpmime/src/main/java/org/apache/http/entity/mime/HttpBrowserCompatibleMultipart.java?rev=1495409&r1=1495408&r2=1495409&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpmime/src/main/java/org/apache/http/entity/mime/HttpBrowserCompatibleMultipart.java (original)
+++ httpcomponents/httpclient/trunk/httpmime/src/main/java/org/apache/http/entity/mime/HttpBrowserCompatibleMultipart.java Fri Jun 21 12:28:58 2013
@@ -30,6 +30,7 @@ package org.apache.http.entity.mime;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.nio.charset.Charset;
+import java.util.List;
 
 /**
  * HttpBrowserCompatibleMultipart represents a collection of MIME multipart encoded
@@ -39,22 +40,20 @@ import java.nio.charset.Charset;
  */
 class HttpBrowserCompatibleMultipart extends AbstractMultipartForm {
 
-    /**
-     * Creates an instance with the specified settings.
-     *
-     * @param subType mime subtype - must not be {@code null}
-     * @param charset the character set to use. May be {@code null},
-     *   in which case {@link MIME#DEFAULT_CHARSET} - i.e. US-ASCII - is used.
-     * @param boundary to use  - must not be {@code null}
-     * @throws IllegalArgumentException if charset is null or boundary is null
-     */
+    private final List<FormBodyPart> parts;
+
     public HttpBrowserCompatibleMultipart(
-            final String subType, final Charset charset, final String boundary) {
+            final String subType,
+            final Charset charset,
+            final String boundary,
+            final List<FormBodyPart> parts) {
         super(subType, charset, boundary);
+        this.parts = parts;
     }
 
-    public HttpBrowserCompatibleMultipart(final String subType, final String boundary) {
-        this(subType, null, boundary);
+    @Override
+    public List<FormBodyPart> getBodyParts() {
+        return this.parts;
     }
 
     /**

Modified: httpcomponents/httpclient/trunk/httpmime/src/main/java/org/apache/http/entity/mime/HttpMultipart.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpmime/src/main/java/org/apache/http/entity/mime/HttpMultipart.java?rev=1495409&r1=1495408&r2=1495409&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpmime/src/main/java/org/apache/http/entity/mime/HttpMultipart.java (original)
+++ httpcomponents/httpclient/trunk/httpmime/src/main/java/org/apache/http/entity/mime/HttpMultipart.java Fri Jun 21 12:28:58 2013
@@ -30,6 +30,8 @@ package org.apache.http.entity.mime;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * HttpMultipart represents a collection of MIME multipart encoded content bodies. This class is
@@ -44,6 +46,7 @@ import java.nio.charset.Charset;
 public class HttpMultipart extends AbstractMultipartForm {
 
     private final HttpMultipartMode mode;
+    private final List<FormBodyPart> parts;
 
     /**
      * Creates an instance with the specified settings.
@@ -60,6 +63,7 @@ public class HttpMultipart extends Abstr
             final HttpMultipartMode mode) {
         super(subType, charset, boundary);
         this.mode = mode;
+        this.parts = new ArrayList<FormBodyPart>();
     }
 
     /**
@@ -107,4 +111,16 @@ public class HttpMultipart extends Abstr
         }
     }
 
+    @Override
+    public List<FormBodyPart> getBodyParts() {
+        return this.parts;
+    }
+
+    public void addBodyPart(final FormBodyPart part) {
+        if (part == null) {
+            return;
+        }
+        this.parts.add(part);
+    }
+
 }

Modified: httpcomponents/httpclient/trunk/httpmime/src/main/java/org/apache/http/entity/mime/HttpRFC6532Multipart.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpmime/src/main/java/org/apache/http/entity/mime/HttpRFC6532Multipart.java?rev=1495409&r1=1495408&r2=1495409&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpmime/src/main/java/org/apache/http/entity/mime/HttpRFC6532Multipart.java (original)
+++ httpcomponents/httpclient/trunk/httpmime/src/main/java/org/apache/http/entity/mime/HttpRFC6532Multipart.java Fri Jun 21 12:28:58 2013
@@ -30,6 +30,7 @@ package org.apache.http.entity.mime;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.nio.charset.Charset;
+import java.util.List;
 
 /**
  * HttpRFC6532Multipart represents a collection of MIME multipart encoded content bodies,
@@ -40,26 +41,22 @@ import java.nio.charset.Charset;
  */
 class HttpRFC6532Multipart extends AbstractMultipartForm {
 
-    /**
-     * Creates an instance with the specified settings.
-     *
-     * @param subType mime subtype - must not be {@code null}
-     * @param charset the character set to use. May be {@code null},
-     *  in which case {@link MIME#DEFAULT_CHARSET} - i.e. US-ASCII - is used.
-     * @param boundary to use  - must not be {@code null}
-     * @throws IllegalArgumentException if charset is null or boundary is null
-     */
-    public HttpRFC6532Multipart(final String subType, final Charset charset, final String boundary) {
+    private final List<FormBodyPart> parts;
+
+    public HttpRFC6532Multipart(
+            final String subType,
+            final Charset charset,
+            final String boundary,
+            final List<FormBodyPart> parts) {
         super(subType, charset, boundary);
+        this.parts = parts;
     }
 
-    public HttpRFC6532Multipart(final String subType, final String boundary) {
-        this(subType, null, boundary);
+    @Override
+    public List<FormBodyPart> getBodyParts() {
+        return this.parts;
     }
 
-    /**
-      * Write the multipart header fields; depends on the style.
-      */
     @Override
     protected void formatMultipartHeader(
         final FormBodyPart part,

Modified: httpcomponents/httpclient/trunk/httpmime/src/main/java/org/apache/http/entity/mime/HttpStrictMultipart.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpmime/src/main/java/org/apache/http/entity/mime/HttpStrictMultipart.java?rev=1495409&r1=1495408&r2=1495409&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpmime/src/main/java/org/apache/http/entity/mime/HttpStrictMultipart.java (original)
+++ httpcomponents/httpclient/trunk/httpmime/src/main/java/org/apache/http/entity/mime/HttpStrictMultipart.java Fri Jun 21 12:28:58 2013
@@ -30,6 +30,7 @@ package org.apache.http.entity.mime;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.nio.charset.Charset;
+import java.util.List;
 
 /**
  * HttpStrictMultipart represents a collection of MIME multipart encoded content bodies,
@@ -40,26 +41,22 @@ import java.nio.charset.Charset;
  */
 class HttpStrictMultipart extends AbstractMultipartForm {
 
-    /**
-     * Creates an instance with the specified settings.
-     *
-     * @param subType mime subtype - must not be {@code null}
-     * @param charset the character set to use. May be {@code null},
-     *  in which case {@link MIME#DEFAULT_CHARSET} - i.e. US-ASCII - is used.
-     * @param boundary to use  - must not be {@code null}
-     * @throws IllegalArgumentException if charset is null or boundary is null
-     */
-    public HttpStrictMultipart(final String subType, final Charset charset, final String boundary) {
+    private final List<FormBodyPart> parts;
+
+    public HttpStrictMultipart(
+            final String subType,
+            final Charset charset,
+            final String boundary,
+            final List<FormBodyPart> parts) {
         super(subType, charset, boundary);
+        this.parts = parts;
     }
 
-    public HttpStrictMultipart(final String subType, final String boundary) {
-        this(subType, null, boundary);
+    @Override
+    public List<FormBodyPart> getBodyParts() {
+        return this.parts;
     }
 
-    /**
-      * Write the multipart header fields; depends on the style.
-      */
     @Override
     protected void formatMultipartHeader(
         final FormBodyPart part,

Modified: httpcomponents/httpclient/trunk/httpmime/src/main/java/org/apache/http/entity/mime/MIME.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpmime/src/main/java/org/apache/http/entity/mime/MIME.java?rev=1495409&r1=1495408&r2=1495409&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpmime/src/main/java/org/apache/http/entity/mime/MIME.java (original)
+++ httpcomponents/httpclient/trunk/httpmime/src/main/java/org/apache/http/entity/mime/MIME.java Fri Jun 21 12:28:58 2013
@@ -27,6 +27,8 @@
 
 package org.apache.http.entity.mime;
 
+import org.apache.http.Consts;
+
 import java.nio.charset.Charset;
 
 /**
@@ -43,9 +45,9 @@ public final class MIME {
     public static final String ENC_BINARY            = "binary";
 
     /** The default character set to be used, i.e. "US-ASCII" */
-    public static final Charset DEFAULT_CHARSET      = Charset.forName("US-ASCII");
+    public static final Charset DEFAULT_CHARSET      = Consts.ASCII;
     
     /** UTF-8 is used for RFC6532 */
-    public static final Charset UTF8_CHARSET    = Charset.forName("UTF-8");
+    public static final Charset UTF8_CHARSET         = Consts.UTF_8;
 
 }

Modified: httpcomponents/httpclient/trunk/httpmime/src/main/java/org/apache/http/entity/mime/MultipartEntity.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpmime/src/main/java/org/apache/http/entity/mime/MultipartEntity.java?rev=1495409&r1=1495408&r2=1495409&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpmime/src/main/java/org/apache/http/entity/mime/MultipartEntity.java (original)
+++ httpcomponents/httpclient/trunk/httpmime/src/main/java/org/apache/http/entity/mime/MultipartEntity.java Fri Jun 21 12:28:58 2013
@@ -36,14 +36,15 @@ import java.util.Random;
 import org.apache.http.Header;
 import org.apache.http.HttpEntity;
 import org.apache.http.entity.mime.content.ContentBody;
-import org.apache.http.message.BasicHeader;
-import org.apache.http.protocol.HTTP;
 
 /**
  * Multipart/form coded HTTP entity consisting of multiple body parts.
  *
  * @since 4.0
+ *
+ * @deprecated 4.3 Use {@link MultipartEntityBuilder}.
  */
+@Deprecated
 public class MultipartEntity implements HttpEntity {
 
     /**
@@ -53,12 +54,8 @@ public class MultipartEntity implements 
         "-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
             .toCharArray();
 
-    private final AbstractMultipartForm multipart;
-    private final Header contentType;
-
-    // @GuardedBy("dirty") // we always read dirty before accessing length
-    private long length;
-    private volatile boolean dirty; // used to decide whether to recalculate length
+    private final MultipartEntityBuilder builder;
+    private volatile MultipartFormEntity entity;
 
     /**
      * Creates an instance using the specified parameters
@@ -71,30 +68,14 @@ public class MultipartEntity implements 
             final String boundary,
             final Charset charset) {
         super();
-        final String b = boundary != null ? boundary : generateBoundary();
-        this.multipart = HttpMultipartFactory.getInstance("form-data", charset, b, mode != null ? mode : HttpMultipartMode.STRICT);
-        this.contentType = new BasicHeader(HTTP.CONTENT_TYPE, generateContentType(b, charset));
-        this.dirty = true;
+        this.builder = new MultipartEntityBuilder()
+                .setMode(mode)
+                .setCharset(charset)
+                .setBoundary(boundary);
+        this.entity = null;
     }
     
     /**
-     * Creates an instance using the specified parameters
-     * @param multipart the part encoder to use, may not be {@code null}
-     * @param boundary the boundary string, may be {@code null}, in which case {@link #generateBoundary()} is invoked to create the string
-     * @param charset the character set to use, may be {@code null}, in which case {@link MIME#DEFAULT_CHARSET} - i.e. US-ASCII - is used.
-     */
-    public MultipartEntity(
-            final AbstractMultipartForm multipart,
-            final String boundary,
-            final Charset charset) {
-        super();
-        final String b = boundary != null ? boundary : generateBoundary();
-        this.multipart = multipart;
-        this.contentType = new BasicHeader(HTTP.CONTENT_TYPE, generateContentType(b, charset));
-        this.dirty = true;
-    }
-
-    /**
      * Creates an instance using the specified {@link HttpMultipartMode} mode.
      * Boundary and charset are set to {@code null}.
      * @param mode the desired mode
@@ -133,16 +114,16 @@ public class MultipartEntity implements 
         return buffer.toString();
     }
 
-    /**
-     * @since 4.3
-     */
-    protected AbstractMultipartForm getMultipart() {
-        return multipart;
+    private MultipartFormEntity getEntity() {
+        if (this.entity == null) {
+            this.entity = this.builder.buildEntity();
+        }
+        return this.entity;
     }
 
     public void addPart(final FormBodyPart bodyPart) {
-        this.multipart.addBodyPart(bodyPart);
-        this.dirty = true;
+        this.builder.addPart(bodyPart);
+        this.entity = null;
     }
 
     public void addPart(final String name, final ContentBody contentBody) {
@@ -150,37 +131,27 @@ public class MultipartEntity implements 
     }
 
     public boolean isRepeatable() {
-        for (final FormBodyPart part: this.multipart.getBodyParts()) {
-            final ContentBody body = part.getBody();
-            if (body.getContentLength() < 0) {
-                return false;
-            }
-        }
-        return true;
+        return getEntity().isRepeatable();
     }
 
     public boolean isChunked() {
-        return !isRepeatable();
+        return getEntity().isChunked();
     }
 
     public boolean isStreaming() {
-        return !isRepeatable();
+        return getEntity().isStreaming();
     }
 
     public long getContentLength() {
-        if (this.dirty) {
-            this.length = this.multipart.getTotalLength();
-            this.dirty = false;
-        }
-        return this.length;
+        return getEntity().getContentLength();
     }
 
     public Header getContentType() {
-        return this.contentType;
+        return getEntity().getContentType();
     }
 
     public Header getContentEncoding() {
-        return null;
+        return getEntity().getContentEncoding();
     }
 
     public void consumeContent()
@@ -197,7 +168,7 @@ public class MultipartEntity implements 
     }
 
     public void writeTo(final OutputStream outstream) throws IOException {
-        this.multipart.writeTo(outstream);
+        getEntity().writeTo(outstream);
     }
 
 }

Modified: httpcomponents/httpclient/trunk/httpmime/src/main/java/org/apache/http/entity/mime/MultipartEntityBuilder.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpmime/src/main/java/org/apache/http/entity/mime/MultipartEntityBuilder.java?rev=1495409&r1=1495408&r2=1495409&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpmime/src/main/java/org/apache/http/entity/mime/MultipartEntityBuilder.java (original)
+++ httpcomponents/httpclient/trunk/httpmime/src/main/java/org/apache/http/entity/mime/MultipartEntityBuilder.java Fri Jun 21 12:28:58 2013
@@ -31,20 +31,36 @@ import java.io.File;
 import java.io.InputStream;
 import java.nio.charset.Charset;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
+import java.util.Random;
 
+import org.apache.http.HttpEntity;
 import org.apache.http.entity.ContentType;
 import org.apache.http.entity.mime.content.ByteArrayBody;
+import org.apache.http.entity.mime.content.ContentBody;
 import org.apache.http.entity.mime.content.FileBody;
 import org.apache.http.entity.mime.content.InputStreamBody;
 import org.apache.http.entity.mime.content.StringBody;
 import org.apache.http.util.Args;
 
 /**
+ * Builder for multipart {@link HttpEntity}s.
+ *
  * @since 4.3
  */
 public class MultipartEntityBuilder {
 
+    /**
+     * The pool of ASCII chars to be used for generating a multipart boundary.
+     */
+    private final static char[] MULTIPART_CHARS =
+            "-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
+                    .toCharArray();
+
+    private final static String DEFAULT_SUBTYPE = "form-data";
+
+    private String subType = DEFAULT_SUBTYPE;
     private HttpMultipartMode mode = HttpMultipartMode.STRICT;
     private String boundary = null;
     private Charset charset = null;
@@ -58,6 +74,11 @@ public class MultipartEntityBuilder {
         super();
     }
 
+    public MultipartEntityBuilder setMode(final HttpMultipartMode mode) {
+        this.mode = mode;
+        return this;
+    }
+
     public MultipartEntityBuilder setLaxMode() {
         this.mode = HttpMultipartMode.BROWSER_COMPATIBLE;
         return this;
@@ -78,17 +99,28 @@ public class MultipartEntityBuilder {
         return this;
     }
 
-    public MultipartEntityBuilder addTextBody(
-            final String name, final String text, final ContentType contentType) {
-        Args.notNull(name, "Name");
-        Args.notNull(text, "Text");
+    MultipartEntityBuilder addPart(final FormBodyPart bodyPart) {
+        if (bodyPart == null) {
+            return this;
+        }
         if (this.bodyParts == null) {
             this.bodyParts = new ArrayList<FormBodyPart>();
         }
-        this.bodyParts.add(new FormBodyPart(name, new StringBody(text, contentType)));
+        this.bodyParts.add(bodyPart);
         return this;
     }
 
+    public MultipartEntityBuilder addPart(final String name, final ContentBody contentBody) {
+        Args.notNull(name, "Name");
+        Args.notNull(contentBody, "Content body");
+        return addPart(new FormBodyPart(name, contentBody));
+    }
+
+    public MultipartEntityBuilder addTextBody(
+            final String name, final String text, final ContentType contentType) {
+        return addPart(name, new StringBody(text, contentType));
+    }
+
     public MultipartEntityBuilder addTextBody(
             final String name, final String text) {
         return addTextBody(name, text, ContentType.DEFAULT_TEXT);
@@ -96,11 +128,7 @@ public class MultipartEntityBuilder {
 
     public MultipartEntityBuilder addBinaryBody(
             final String name, final byte[] b, final ContentType contentType, final String filename) {
-        if (this.bodyParts == null) {
-            this.bodyParts = new ArrayList<FormBodyPart>();
-        }
-        this.bodyParts.add(new FormBodyPart(name, new ByteArrayBody(b, contentType, filename)));
-        return this;
+        return addPart(name, new ByteArrayBody(b, contentType, filename));
     }
 
     public MultipartEntityBuilder addBinaryBody(
@@ -110,12 +138,7 @@ public class MultipartEntityBuilder {
 
     public MultipartEntityBuilder addBinaryBody(
             final String name, final File file, final ContentType contentType, final String filename) {
-        if (this.bodyParts == null) {
-            this.bodyParts = new ArrayList<FormBodyPart>();
-        }
-        this.bodyParts.add(
-                new FormBodyPart(name, new FileBody(file, contentType, filename)));
-        return this;
+        return addPart(name, new FileBody(file, contentType, filename));
     }
 
     public MultipartEntityBuilder addBinaryBody(
@@ -126,28 +149,59 @@ public class MultipartEntityBuilder {
     public MultipartEntityBuilder addBinaryBody(
             final String name, final InputStream stream, final ContentType contentType,
             final String filename) {
-        if (this.bodyParts == null) {
-            this.bodyParts = new ArrayList<FormBodyPart>();
-        }
-        this.bodyParts.add(
-                new FormBodyPart(name, new InputStreamBody(stream, contentType, filename)));
-        return this;
+        return addPart(name, new InputStreamBody(stream, contentType, filename));
     }
 
     public MultipartEntityBuilder addBinaryBody(final String name, final InputStream stream) {
         return addBinaryBody(name, stream, ContentType.DEFAULT_BINARY, null);
     }
 
-    public MultipartEntity build() {
-        final MultipartEntity e = new MultipartEntity(
-                this.mode,
-                this.boundary, this.charset);
-        if (this.bodyParts != null) {
-            for (final FormBodyPart bp: this.bodyParts) {
-                e.addPart(bp);
-            }
+    private String generateContentType(
+            final String boundary,
+            final Charset charset) {
+        final StringBuilder buffer = new StringBuilder();
+        buffer.append("multipart/form-data; boundary=");
+        buffer.append(boundary);
+        if (charset != null) {
+            buffer.append("; charset=");
+            buffer.append(charset.name());
         }
-        return e;
+        return buffer.toString();
+    }
+
+    private String generateBoundary() {
+        final StringBuilder buffer = new StringBuilder();
+        final Random rand = new Random();
+        final int count = rand.nextInt(11) + 30; // a random size from 30 to 40
+        for (int i = 0; i < count; i++) {
+            buffer.append(MULTIPART_CHARS[rand.nextInt(MULTIPART_CHARS.length)]);
+        }
+        return buffer.toString();
+    }
+
+    MultipartFormEntity buildEntity() {
+        final String st = subType != null ? subType : DEFAULT_SUBTYPE;
+        final Charset cs = charset;
+        final String b = boundary != null ? boundary : generateBoundary();
+        final List<FormBodyPart> bps = bodyParts != null ? new ArrayList<FormBodyPart>(bodyParts) :
+                Collections.<FormBodyPart>emptyList();
+        final HttpMultipartMode m = mode != null ? mode : HttpMultipartMode.STRICT;
+        final AbstractMultipartForm form;
+        switch (m) {
+            case BROWSER_COMPATIBLE:
+                form = new HttpBrowserCompatibleMultipart(st, cs, b, bps);
+                break;
+            case RFC6532:
+                form = new HttpRFC6532Multipart(st, cs, b, bps);
+                break;
+            default:
+                form = new HttpStrictMultipart(st, cs, b, bps);
+        }
+        return new MultipartFormEntity(form, generateContentType(b, cs), form.getTotalLength());
+    }
+
+    public HttpEntity build() {
+        return buildEntity();
     }
 
 }

Added: httpcomponents/httpclient/trunk/httpmime/src/main/java/org/apache/http/entity/mime/MultipartFormEntity.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpmime/src/main/java/org/apache/http/entity/mime/MultipartFormEntity.java?rev=1495409&view=auto
==============================================================================
--- httpcomponents/httpclient/trunk/httpmime/src/main/java/org/apache/http/entity/mime/MultipartFormEntity.java (added)
+++ httpcomponents/httpclient/trunk/httpmime/src/main/java/org/apache/http/entity/mime/MultipartFormEntity.java Fri Jun 21 12:28:58 2013
@@ -0,0 +1,100 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * 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.http.entity.mime;
+
+import org.apache.http.Header;
+import org.apache.http.HttpEntity;
+import org.apache.http.message.BasicHeader;
+import org.apache.http.protocol.HTTP;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+class MultipartFormEntity implements HttpEntity {
+
+    private final AbstractMultipartForm multipart;
+    private final Header contentType;
+    private final long contentLength;
+
+    MultipartFormEntity(
+            final AbstractMultipartForm multipart,
+            final String contentType,
+            final long contentLength) {
+        super();
+        this.multipart = multipart;
+        this.contentType = new BasicHeader(HTTP.CONTENT_TYPE, contentType);
+        this.contentLength = contentLength;
+    }
+
+    AbstractMultipartForm getMultipart() {
+        return this.multipart;
+    }
+
+    public boolean isRepeatable() {
+        return this.contentLength != -1;
+    }
+
+    public boolean isChunked() {
+        return !isRepeatable();
+    }
+
+    public boolean isStreaming() {
+        return !isRepeatable();
+    }
+
+    public long getContentLength() {
+        return this.contentLength;
+    }
+
+    public Header getContentType() {
+        return this.contentType;
+    }
+
+    public Header getContentEncoding() {
+        return null;
+    }
+
+    public void consumeContent()
+        throws IOException, UnsupportedOperationException{
+        if (isStreaming()) {
+            throw new UnsupportedOperationException(
+                    "Streaming entity does not implement #consumeContent()");
+        }
+    }
+
+    public InputStream getContent() throws IOException {
+        throw new UnsupportedOperationException(
+                    "Multipart form entity does not implement #getContent()");
+    }
+
+    public void writeTo(final OutputStream outstream) throws IOException {
+        this.multipart.writeTo(outstream);
+    }
+
+}

Propchange: httpcomponents/httpclient/trunk/httpmime/src/main/java/org/apache/http/entity/mime/MultipartFormEntity.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: httpcomponents/httpclient/trunk/httpmime/src/main/java/org/apache/http/entity/mime/MultipartFormEntity.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: httpcomponents/httpclient/trunk/httpmime/src/main/java/org/apache/http/entity/mime/MultipartFormEntity.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: httpcomponents/httpclient/trunk/httpmime/src/test/java/org/apache/http/entity/mime/TestMultipartEntityBuilder.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpmime/src/test/java/org/apache/http/entity/mime/TestMultipartEntityBuilder.java?rev=1495409&r1=1495408&r2=1495409&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpmime/src/test/java/org/apache/http/entity/mime/TestMultipartEntityBuilder.java (original)
+++ httpcomponents/httpclient/trunk/httpmime/src/test/java/org/apache/http/entity/mime/TestMultipartEntityBuilder.java Fri Jun 21 12:28:58 2013
@@ -39,33 +39,33 @@ public class TestMultipartEntityBuilder 
 
     @Test
     public void testBasics() throws Exception {
-        final MultipartEntity entity = MultipartEntityBuilder.create().build();
+        final MultipartFormEntity entity = MultipartEntityBuilder.create().buildEntity();
         Assert.assertNotNull(entity);
-        Assert.assertEquals("org.apache.http.entity.mime.HttpStrictMultipart", entity.getMultipart().getClass().getName());
+        Assert.assertTrue(entity.getMultipart() instanceof HttpStrictMultipart);
         Assert.assertEquals(0, entity.getMultipart().getBodyParts().size());
     }
 
     @Test
     public void testMultipartOptions() throws Exception {
-        final MultipartEntity entity = MultipartEntityBuilder.create()
+        final MultipartFormEntity entity = MultipartEntityBuilder.create()
                 .setBoundary("blah-blah")
                 .setCharset(Consts.UTF_8)
                 .setLaxMode()
-                .build();
+                .buildEntity();
         Assert.assertNotNull(entity);
-        Assert.assertEquals("org.apache.http.entity.mime.HttpBrowserCompatibleMultipart", entity.getMultipart().getClass().getName());
+        Assert.assertTrue(entity.getMultipart() instanceof HttpBrowserCompatibleMultipart);
         Assert.assertEquals("blah-blah", entity.getMultipart().getBoundary());
         Assert.assertEquals(Consts.UTF_8, entity.getMultipart().getCharset());
     }
 
     @Test
     public void testAddBodyParts() throws Exception {
-        final MultipartEntity entity = MultipartEntityBuilder.create()
+        final MultipartFormEntity entity = MultipartEntityBuilder.create()
                 .addTextBody("p1", "stuff")
                 .addBinaryBody("p2", new File("stuff"))
                 .addBinaryBody("p3", new byte[] {})
                 .addBinaryBody("p4", new ByteArrayInputStream(new byte[] {}))
-                .build();
+                .buildEntity();
         Assert.assertNotNull(entity);
         final List<FormBodyPart> bodyParts = entity.getMultipart().getBodyParts();
         Assert.assertNotNull(bodyParts);

Modified: httpcomponents/httpclient/trunk/httpmime/src/test/java/org/apache/http/entity/mime/TestMultipartForm.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpmime/src/test/java/org/apache/http/entity/mime/TestMultipartForm.java?rev=1495409&r1=1495408&r2=1495409&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpmime/src/test/java/org/apache/http/entity/mime/TestMultipartForm.java (original)
+++ httpcomponents/httpclient/trunk/httpmime/src/test/java/org/apache/http/entity/mime/TestMultipartForm.java Fri Jun 21 12:28:58 2013
@@ -33,20 +33,30 @@ import java.io.FileInputStream;
 import java.io.FileWriter;
 import java.io.Writer;
 import java.nio.charset.Charset;
+import java.util.Arrays;
 
 import org.apache.http.Consts;
 import org.apache.http.entity.ContentType;
 import org.apache.http.entity.mime.content.FileBody;
 import org.apache.http.entity.mime.content.InputStreamBody;
 import org.apache.http.entity.mime.content.StringBody;
+import org.junit.After;
 import org.junit.Assert;
 import org.junit.Test;
 
 public class TestMultipartForm {
 
+    private File tmpfile;
+
+    @After
+    public void cleanup() {
+        if (tmpfile != null) {
+            tmpfile.delete();
+        }
+    }
+
     @Test
     public void testMultipartFormStringParts() throws Exception {
-        final AbstractMultipartForm multipart = new HttpStrictMultipart("form-data", "foo");
         final FormBodyPart p1 = new FormBodyPart(
                 "field1",
                 new StringBody("this stuff", ContentType.DEFAULT_TEXT));
@@ -57,10 +67,8 @@ public class TestMultipartForm {
         final FormBodyPart p3 = new FormBodyPart(
                 "field3",
                 new StringBody("all kind of stuff", ContentType.DEFAULT_TEXT));
-
-        multipart.addBodyPart(p1);
-        multipart.addBodyPart(p2);
-        multipart.addBodyPart(p3);
+        final HttpStrictMultipart multipart = new HttpStrictMultipart("form-data", null, "foo",
+                Arrays.asList(p1, p2, p3));
 
         final ByteArrayOutputStream out = new ByteArrayOutputStream();
         multipart.writeTo(out);
@@ -93,8 +101,7 @@ public class TestMultipartForm {
 
     @Test
     public void testMultipartFormBinaryParts() throws Exception {
-        final File tmpfile = File.createTempFile("tmp", ".bin");
-        tmpfile.deleteOnExit();
+        tmpfile = File.createTempFile("tmp", ".bin");
         final Writer writer = new FileWriter(tmpfile);
         try {
             writer.append("some random whatever");
@@ -102,16 +109,14 @@ public class TestMultipartForm {
             writer.close();
         }
 
-        final AbstractMultipartForm multipart = new HttpStrictMultipart("form-data", "foo");
         final FormBodyPart p1 = new FormBodyPart(
                 "field1",
                 new FileBody(tmpfile));
         final FormBodyPart p2 = new FormBodyPart(
                 "field2",
                 new InputStreamBody(new FileInputStream(tmpfile), "file.tmp"));
-
-        multipart.addBodyPart(p1);
-        multipart.addBodyPart(p2);
+        final HttpStrictMultipart multipart = new HttpStrictMultipart("form-data", null, "foo",
+                Arrays.asList(p1, p2));
 
         final ByteArrayOutputStream out = new ByteArrayOutputStream();
         multipart.writeTo(out);
@@ -136,14 +141,11 @@ public class TestMultipartForm {
         final String s = out.toString("US-ASCII");
         Assert.assertEquals(expected, s);
         Assert.assertEquals(-1, multipart.getTotalLength());
-
-        tmpfile.delete();
     }
 
     @Test
-    public void testMultipartFormBrowserCompatible() throws Exception {
-        final File tmpfile = File.createTempFile("tmp", ".bin");
-        tmpfile.deleteOnExit();
+    public void testMultipartFormStrict() throws Exception {
+        tmpfile = File.createTempFile("tmp", ".bin");
         final Writer writer = new FileWriter(tmpfile);
         try {
             writer.append("some random whatever");
@@ -151,8 +153,6 @@ public class TestMultipartForm {
             writer.close();
         }
 
-        // Strict is no accident here, despite the test name - otherwise Transfer-Encoding is not produced.
-        final AbstractMultipartForm multipart = new HttpStrictMultipart("form-data", null, "foo");
         final FormBodyPart p1 = new FormBodyPart(
                 "field1",
                 new FileBody(tmpfile));
@@ -162,10 +162,8 @@ public class TestMultipartForm {
         final FormBodyPart p3 = new FormBodyPart(
                 "field3",
                 new InputStreamBody(new FileInputStream(tmpfile), "file.tmp"));
-
-        multipart.addBodyPart(p1);
-        multipart.addBodyPart(p2);
-        multipart.addBodyPart(p3);
+        final HttpStrictMultipart multipart = new HttpStrictMultipart("form-data", null, "foo",
+                Arrays.asList(p1, p2, p3));
 
         final ByteArrayOutputStream out = new ByteArrayOutputStream();
         multipart.writeTo(out);
@@ -197,14 +195,11 @@ public class TestMultipartForm {
         final String s = out.toString("US-ASCII");
         Assert.assertEquals(expected, s);
         Assert.assertEquals(-1, multipart.getTotalLength());
-
-        tmpfile.delete();
     }
 
     @Test
     public void testMultipartFormRFC6532() throws Exception {
-        final File tmpfile = File.createTempFile("tmp", ".bin");
-        tmpfile.deleteOnExit();
+        tmpfile = File.createTempFile("tmp", ".bin");
         final Writer writer = new FileWriter(tmpfile);
         try {
             writer.append("some random whatever");
@@ -212,8 +207,6 @@ public class TestMultipartForm {
             writer.close();
         }
 
-        // Strict is no accident here, despite the test name - otherwise Transfer-Encoding is not produced.
-        final AbstractMultipartForm multipart = new HttpRFC6532Multipart("form-data", null, "foo");
         final FormBodyPart p1 = new FormBodyPart(
                 "field1\u0414",
                 new FileBody(tmpfile));
@@ -223,10 +216,8 @@ public class TestMultipartForm {
         final FormBodyPart p3 = new FormBodyPart(
                 "field3",
                 new InputStreamBody(new FileInputStream(tmpfile), "file.tmp"));
-
-        multipart.addBodyPart(p1);
-        multipart.addBodyPart(p2);
-        multipart.addBodyPart(p3);
+        final HttpRFC6532Multipart multipart = new HttpRFC6532Multipart("form-data", null, "foo",
+                Arrays.asList(p1, p2, p3));
 
         final ByteArrayOutputStream out = new ByteArrayOutputStream();
         multipart.writeTo(out);
@@ -258,8 +249,6 @@ public class TestMultipartForm {
         final String s = out.toString("UTF-8");
         Assert.assertEquals(expected, s);
         Assert.assertEquals(-1, multipart.getTotalLength());
-
-        tmpfile.delete();
     }
 
     private static final int SWISS_GERMAN_HELLO [] = {
@@ -286,8 +275,7 @@ public class TestMultipartForm {
         final String s1 = constructString(SWISS_GERMAN_HELLO);
         final String s2 = constructString(RUSSIAN_HELLO);
 
-        final File tmpfile = File.createTempFile("tmp", ".bin");
-        tmpfile.deleteOnExit();
+        tmpfile = File.createTempFile("tmp", ".bin");
         final Writer writer = new FileWriter(tmpfile);
         try {
             writer.append("some random whatever");
@@ -295,16 +283,15 @@ public class TestMultipartForm {
             writer.close();
         }
 
-        final AbstractMultipartForm multipart = new HttpBrowserCompatibleMultipart("form-data", Charset.forName("UTF-8"), "foo");
         final FormBodyPart p1 = new FormBodyPart(
                 "field1",
                 new InputStreamBody(new FileInputStream(tmpfile), s1 + ".tmp"));
         final FormBodyPart p2 = new FormBodyPart(
                 "field2",
                 new InputStreamBody(new FileInputStream(tmpfile), s2 + ".tmp"));
-
-        multipart.addBodyPart(p1);
-        multipart.addBodyPart(p2);
+        final HttpBrowserCompatibleMultipart multipart = new HttpBrowserCompatibleMultipart(
+                "form-data", Consts.UTF_8, "foo",
+                Arrays.asList(p1, p2));
 
         final ByteArrayOutputStream out = new ByteArrayOutputStream();
         multipart.writeTo(out);
@@ -327,8 +314,6 @@ public class TestMultipartForm {
         final String s = out.toString("UTF-8");
         Assert.assertEquals(expected, s);
         Assert.assertEquals(-1, multipart.getTotalLength());
-
-        tmpfile.delete();
     }
 
     @Test
@@ -336,16 +321,14 @@ public class TestMultipartForm {
         final String s1 = constructString(SWISS_GERMAN_HELLO);
         final String s2 = constructString(RUSSIAN_HELLO);
 
-        final AbstractMultipartForm multipart = new HttpStrictMultipart("form-data", "foo");
         final FormBodyPart p1 = new FormBodyPart(
                 "field1",
                 new StringBody(s1, ContentType.create("text/plain", Charset.forName("ISO-8859-1"))));
         final FormBodyPart p2 = new FormBodyPart(
                 "field2",
                 new StringBody(s2, ContentType.create("text/plain", Charset.forName("KOI8-R"))));
-
-        multipart.addBodyPart(p1);
-        multipart.addBodyPart(p2);
+        final HttpStrictMultipart multipart = new HttpStrictMultipart("form-data", null, "foo",
+                Arrays.asList(p1, p2));
 
         final ByteArrayOutputStream out1 = new ByteArrayOutputStream();
         multipart.writeTo(out1);

Modified: httpcomponents/httpclient/trunk/httpmime/src/test/java/org/apache/http/entity/mime/TestMultipartFormHttpEntity.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpmime/src/test/java/org/apache/http/entity/mime/TestMultipartFormHttpEntity.java?rev=1495409&r1=1495408&r2=1495409&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpmime/src/test/java/org/apache/http/entity/mime/TestMultipartFormHttpEntity.java (original)
+++ httpcomponents/httpclient/trunk/httpmime/src/test/java/org/apache/http/entity/mime/TestMultipartFormHttpEntity.java Fri Jun 21 12:28:58 2013
@@ -29,14 +29,13 @@ package org.apache.http.entity.mime;
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
-import java.nio.charset.Charset;
 
 import org.apache.http.Header;
 import org.apache.http.HeaderElement;
+import org.apache.http.HttpEntity;
 import org.apache.http.NameValuePair;
 import org.apache.http.entity.ContentType;
 import org.apache.http.entity.mime.content.InputStreamBody;
-import org.apache.http.entity.mime.content.StringBody;
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -44,10 +43,11 @@ public class TestMultipartFormHttpEntity
 
     @Test
     public void testExplictContractorParams() throws Exception {
-        final MultipartEntity entity = new MultipartEntity(
-                HttpMultipartMode.BROWSER_COMPATIBLE,
-                "whatever",
-                Charset.forName("UTF-8"));
+        final HttpEntity entity = MultipartEntityBuilder.create()
+                .setLaxMode()
+                .setBoundary("whatever")
+                .setCharset(MIME.UTF8_CHARSET)
+                .build();
 
         Assert.assertNull(entity.getContentEncoding());
         Assert.assertNotNull(entity.getContentType());
@@ -68,7 +68,7 @@ public class TestMultipartFormHttpEntity
 
     @Test
     public void testImplictContractorParams() throws Exception {
-        final MultipartEntity entity = new MultipartEntity();
+        final HttpEntity entity = MultipartEntityBuilder.create().build();
         Assert.assertNull(entity.getContentEncoding());
         Assert.assertNotNull(entity.getContentType());
         final Header header = entity.getContentType();
@@ -93,9 +93,10 @@ public class TestMultipartFormHttpEntity
 
     @Test
     public void testRepeatable() throws Exception {
-        final MultipartEntity entity = new MultipartEntity();
-        entity.addPart("p1", new StringBody("blah blah", ContentType.DEFAULT_TEXT));
-        entity.addPart("p2", new StringBody("yada yada", ContentType.DEFAULT_TEXT));
+        final HttpEntity entity = MultipartEntityBuilder.create()
+                .addTextBody("p1", "blah blah", ContentType.DEFAULT_TEXT)
+                .addTextBody("p2", "yada yada", ContentType.DEFAULT_TEXT)
+                .build();
         Assert.assertTrue(entity.isRepeatable());
         Assert.assertFalse(entity.isChunked());
         Assert.assertFalse(entity.isStreaming());
@@ -124,11 +125,12 @@ public class TestMultipartFormHttpEntity
 
     @Test
     public void testNonRepeatable() throws Exception {
-        final MultipartEntity entity = new MultipartEntity();
-        entity.addPart("p1", new InputStreamBody(
-                new ByteArrayInputStream("blah blah".getBytes()), ContentType.DEFAULT_BINARY));
-        entity.addPart("p2", new InputStreamBody(
-                new ByteArrayInputStream("yada yada".getBytes()), ContentType.DEFAULT_BINARY));
+        final HttpEntity entity = MultipartEntityBuilder.create()
+            .addPart("p1", new InputStreamBody(
+                new ByteArrayInputStream("blah blah".getBytes()), ContentType.DEFAULT_BINARY))
+            .addPart("p2", new InputStreamBody(
+                new ByteArrayInputStream("yada yada".getBytes()), ContentType.DEFAULT_BINARY))
+            .build();
         Assert.assertFalse(entity.isRepeatable());
         Assert.assertTrue(entity.isChunked());
         Assert.assertTrue(entity.isStreaming());