diff --git a/blobstore/src/main/java/org/jclouds/blobstore/options/PutOptions.java b/blobstore/src/main/java/org/jclouds/blobstore/options/PutOptions.java
index 70adcb777a..b8af2be7c0 100644
--- a/blobstore/src/main/java/org/jclouds/blobstore/options/PutOptions.java
+++ b/blobstore/src/main/java/org/jclouds/blobstore/options/PutOptions.java
@@ -77,23 +77,39 @@ public class PutOptions implements Cloneable {
}
/**
- * split large blobs into pieces, if supported by the provider
+ * split large blobs into pieces, if supported by the provider.
+ *
+ * Equivalent to multipart(true)
*/
public PutOptions multipart() {
- this.multipart = true;
+ return multipart(true);
+ }
+
+ /**
+ * whether to split large blobs into pieces, if supported by the provider
+ */
+ public PutOptions multipart(boolean val) {
+ this.multipart = val;
return this;
}
public static class Builder {
+ public static PutOptions fromPutOptions(PutOptions putOptions) {
+ return multipart(putOptions.multipart);
+ }
+
/**
* @see PutOptions#multipart()
*/
public static PutOptions multipart() {
- PutOptions options = new PutOptions();
- return options.multipart();
+ return multipart(true);
}
+ public static PutOptions multipart(boolean val) {
+ PutOptions options = new PutOptions();
+ return options.multipart(val);
+ }
}
@Override
diff --git a/providers/aws-s3/src/main/java/org/jclouds/aws/s3/blobstore/strategy/internal/ParallelMultipartUploadStrategy.java b/providers/aws-s3/src/main/java/org/jclouds/aws/s3/blobstore/strategy/internal/ParallelMultipartUploadStrategy.java
index d16cca3c5d..bee6d98174 100644
--- a/providers/aws-s3/src/main/java/org/jclouds/aws/s3/blobstore/strategy/internal/ParallelMultipartUploadStrategy.java
+++ b/providers/aws-s3/src/main/java/org/jclouds/aws/s3/blobstore/strategy/internal/ParallelMultipartUploadStrategy.java
@@ -241,7 +241,11 @@ public class ParallelMultipartUploadStrategy implements AsyncMultipartUploadStra
throw rtex;
}
} else {
- ListenableFuture futureETag = ablobstore.putBlob(container, blob, options);
+ // Issue 936: don't just call putBlob, as that will see options=multiPart and
+ // recursively call this execute method again; instead mark as not multipart
+ // because it can all fit in one go.
+ PutOptions nonMultipartOptions = PutOptions.Builder.multipart(false);
+ ListenableFuture futureETag = ablobstore.putBlob(container, blob, nonMultipartOptions);
return maxTime != null ?
futureETag.get(maxTime,TimeUnit.SECONDS) : futureETag.get();
}
diff --git a/providers/aws-s3/src/test/java/org/jclouds/aws/s3/AWSS3ClientLiveTest.java b/providers/aws-s3/src/test/java/org/jclouds/aws/s3/AWSS3ClientLiveTest.java
index cde3e84eab..61339bcd6a 100644
--- a/providers/aws-s3/src/test/java/org/jclouds/aws/s3/AWSS3ClientLiveTest.java
+++ b/providers/aws-s3/src/test/java/org/jclouds/aws/s3/AWSS3ClientLiveTest.java
@@ -34,6 +34,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.util.zip.GZIPInputStream;
+import org.jclouds.blobstore.AsyncBlobStore;
import org.jclouds.blobstore.BlobStore;
import org.jclouds.blobstore.KeyNotFoundException;
import org.jclouds.blobstore.domain.Blob;
@@ -44,9 +45,9 @@ import org.jclouds.s3.S3Client;
import org.jclouds.s3.S3ClientLiveTest;
import org.jclouds.s3.domain.ListBucketResponse;
import org.jclouds.s3.domain.ObjectMetadata;
+import org.jclouds.s3.domain.ObjectMetadata.StorageClass;
import org.jclouds.s3.domain.ObjectMetadataBuilder;
import org.jclouds.s3.domain.S3Object;
-import org.jclouds.s3.domain.ObjectMetadata.StorageClass;
import org.testng.ITestContext;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
@@ -161,6 +162,20 @@ public class AWSS3ClientLiveTest extends S3ClientLiveTest {
}
}
+ public void testMultipartAsynchronouslySmallBlob() throws IOException, InterruptedException, Exception {
+ String containerName = getContainerName();
+
+ try {
+ AsyncBlobStore asyncBlobStore = view.getAsyncBlobStore();
+ asyncBlobStore.createContainerInLocation(null, containerName).get();
+ Blob blob = asyncBlobStore.blobBuilder("small").payload("small").build();
+ asyncBlobStore.putBlob(containerName, blob, PutOptions.Builder.multipart()).get();
+
+ } finally {
+ returnContainer(containerName);
+ }
+ }
+
public void testPutWithReducedRedundancyStorage() throws InterruptedException {
String containerName = getContainerName();
try {