Repository: jclouds-labs-google Updated Branches: refs/heads/1.8.x 155b3eb78 -> 853e55ebd
JCLOUDS-458: Multipart Related Upload Project: http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/repo Commit: http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/commit/18114754 Tree: http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/tree/18114754 Diff: http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/diff/18114754 Branch: refs/heads/1.8.x Commit: 18114754da04aceadebbbcec3ac1efffa1c7b5e5 Parents: 8e145af Author: hsbhathiya <[email protected]> Authored: Fri Aug 29 01:45:03 2014 +0530 Committer: Andrew Gaul <[email protected]> Committed: Wed Sep 10 15:04:41 2014 -0700 ---------------------------------------------------------------------- google-cloud-storage/pom.xml | 6 ++ .../binders/MultipartUploadBinder.java | 71 ++++++++++++++++++++ .../googlecloudstorage/features/ObjectApi.java | 24 +++++++ .../features/ObjectApiLiveTest.java | 47 ++++++++++++- 4 files changed, 147 insertions(+), 1 deletion(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/18114754/google-cloud-storage/pom.xml ---------------------------------------------------------------------- diff --git a/google-cloud-storage/pom.xml b/google-cloud-storage/pom.xml index d26a279..9cbbf95 100644 --- a/google-cloud-storage/pom.xml +++ b/google-cloud-storage/pom.xml @@ -90,6 +90,12 @@ <artifactId>logback-classic</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>org.assertj</groupId> + <artifactId>assertj-core</artifactId> + <version>1.6.1</version> + <scope>test</scope> + </dependency> </dependencies> <profiles> <profile> http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/18114754/google-cloud-storage/src/main/java/org/jclouds/googlecloudstorage/binders/MultipartUploadBinder.java ---------------------------------------------------------------------- diff --git a/google-cloud-storage/src/main/java/org/jclouds/googlecloudstorage/binders/MultipartUploadBinder.java b/google-cloud-storage/src/main/java/org/jclouds/googlecloudstorage/binders/MultipartUploadBinder.java new file mode 100644 index 0000000..b286d28 --- /dev/null +++ b/google-cloud-storage/src/main/java/org/jclouds/googlecloudstorage/binders/MultipartUploadBinder.java @@ -0,0 +1,71 @@ +/* + * 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. + */ +package org.jclouds.googlecloudstorage.binders; + +import java.util.Map; + +import javax.ws.rs.core.HttpHeaders; +import javax.ws.rs.core.MediaType; + +import org.jclouds.googlecloudstorage.domain.templates.ObjectTemplate; +import org.jclouds.http.HttpRequest; +import org.jclouds.io.Payload; +import org.jclouds.io.Payloads; +import org.jclouds.io.payloads.MultipartForm; +import org.jclouds.io.payloads.Part; +import org.jclouds.io.payloads.StringPayload; +import org.jclouds.rest.MapBinder; + +import com.google.gson.Gson; + +import static com.google.common.base.Preconditions.checkNotNull; + +public class MultipartUploadBinder implements MapBinder { + + private final String BOUNDARY_HEADER = "multipart_boundary"; + + @Override + public <R extends HttpRequest> R bindToRequest(R request, Map<String, Object> postParams) + throws IllegalArgumentException { + + ObjectTemplate template = (ObjectTemplate) postParams.get("template"); + Payload payload = (Payload) postParams.get("payload"); + + String contentType = checkNotNull(template.getContentType(), "contentType"); + Long length = checkNotNull(template.getSize(), "contetLength"); + + StringPayload jsonPayload = Payloads.newStringPayload(new Gson().toJson(template)); + + payload.getContentMetadata().setContentLength(length); + + Part jsonPart = Part.create("Metadata", jsonPayload, + new Part.PartOptions().contentType(MediaType.APPLICATION_JSON)); + Part mediaPart = Part.create(template.getName(), payload, new Part.PartOptions().contentType(contentType)); + + MultipartForm compPayload = new MultipartForm(BOUNDARY_HEADER, jsonPart, mediaPart); + request.setPayload(compPayload); + // HeaderPart + request.toBuilder().replaceHeader(HttpHeaders.CONTENT_TYPE, "Multipart/related; boundary= " + BOUNDARY_HEADER) + .build(); + return request; + } + + @Override + public <R extends HttpRequest> R bindToRequest(R request, Object input) { + return request; + } +} http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/18114754/google-cloud-storage/src/main/java/org/jclouds/googlecloudstorage/features/ObjectApi.java ---------------------------------------------------------------------- diff --git a/google-cloud-storage/src/main/java/org/jclouds/googlecloudstorage/features/ObjectApi.java b/google-cloud-storage/src/main/java/org/jclouds/googlecloudstorage/features/ObjectApi.java index a04d9ba..11299c1 100644 --- a/google-cloud-storage/src/main/java/org/jclouds/googlecloudstorage/features/ObjectApi.java +++ b/google-cloud-storage/src/main/java/org/jclouds/googlecloudstorage/features/ObjectApi.java @@ -35,6 +35,7 @@ import org.jclouds.Fallbacks.FalseOnNotFoundOr404; import org.jclouds.Fallbacks.NullOnNotFoundOr404; import org.jclouds.Fallbacks.TrueOnNotFoundOr404; import org.jclouds.googlecloudstorage.binders.ComposeObjectBinder; +import org.jclouds.googlecloudstorage.binders.MultipartUploadBinder; import org.jclouds.googlecloudstorage.binders.UploadBinder; import org.jclouds.googlecloudstorage.domain.GCSObject; import org.jclouds.googlecloudstorage.domain.ListPage; @@ -459,4 +460,27 @@ public interface ObjectApi { @PathParam("destinationObject") String destinationObject, @PathParam("sourceBucket") String sourceBucket, @PathParam("sourceObject") String sourceObject, CopyObjectOptions options); + /** + * Stores a new object with metadata. + * + * @see https://developers.google.com/storage/docs/json_api/v1/how-tos/upload#multipart + * + * @param bucketName + * Name of the bucket in which the object to be stored + * @param objectTemplate + * Supply an {@link ObjectTemplate}. + * + * @return a {@link GCSObject} + */ + + @Named("Object:multipartUpload") + @POST + @QueryParams(keys = "uploadType", values = "multipart") + @Consumes(MediaType.APPLICATION_JSON) + @Path("/upload/storage/v1/b/{bucket}/o") + @OAuthScopes(STORAGE_FULLCONTROL_SCOPE) + @MapBinder(MultipartUploadBinder.class) + GCSObject multipartUpload(@PathParam("bucket") String bucketName, + @PayloadParam("template") ObjectTemplate objectTemplate, @PayloadParam("payload") Payload payload); + } http://git-wip-us.apache.org/repos/asf/jclouds-labs-google/blob/18114754/google-cloud-storage/src/test/java/org/jclouds/googlecloudstorage/features/ObjectApiLiveTest.java ---------------------------------------------------------------------- diff --git a/google-cloud-storage/src/test/java/org/jclouds/googlecloudstorage/features/ObjectApiLiveTest.java b/google-cloud-storage/src/test/java/org/jclouds/googlecloudstorage/features/ObjectApiLiveTest.java index 854a64f..a454c48 100644 --- a/google-cloud-storage/src/test/java/org/jclouds/googlecloudstorage/features/ObjectApiLiveTest.java +++ b/google-cloud-storage/src/test/java/org/jclouds/googlecloudstorage/features/ObjectApiLiveTest.java @@ -18,6 +18,8 @@ package org.jclouds.googlecloudstorage.features; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotNull; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.entry; import java.io.IOException; import java.util.Set; @@ -55,6 +57,7 @@ import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import com.beust.jcommander.internal.Sets; +import com.google.common.collect.ImmutableMap; import com.google.common.hash.HashCode; import com.google.common.hash.HashFunction; import com.google.common.hash.Hashing; @@ -66,6 +69,7 @@ public class ObjectApiLiveTest extends BaseGoogleCloudStorageApiLiveTest { private static final String BUCKET_NAME2 = "jcloudobjectdestination" + UUID.randomUUID(); private static final String UPLOAD_OBJECT_NAME = "objectOperation.txt"; private static final String UPLOAD_OBJECT_NAME2 = "jcloudslogo.jpg"; + private static final String MULTIPART_UPLOAD_OBJECT = "multipart_related.jpg"; private static final String COPIED_OBJECT_NAME = "copyofObjectOperation.txt"; private static final String COMPOSED_OBJECT = "ComposedObject1.txt"; private static final String COMPOSED_OBJECT2 = "ComposedObject2.json"; @@ -196,7 +200,7 @@ public class ObjectApiLiveTest extends BaseGoogleCloudStorageApiLiveTest { assertEquals(gcsObject.getName(), COPIED_OBJECT_NAME); assertEquals(gcsObject.getContentType(), "text/plain"); - //Test for data + // Test for data PayloadEnclosingImpl impl = api().download(BUCKET_NAME2, COPIED_OBJECT_NAME); assertNotNull(impl); @@ -365,12 +369,53 @@ public class ObjectApiLiveTest extends BaseGoogleCloudStorageApiLiveTest { } @Test(groups = "live", dependsOnMethods = "testPatchObjectsWithOptions") + public void testMultipartJpegUpload() throws IOException { + long contentLength = 32 * 1024L; + ByteSource byteSource = TestUtils.randomByteSource().slice(0, contentLength); + ByteSourcePayload payload = Payloads.newByteSourcePayload(byteSource); + PayloadEnclosingImpl payloadImpl = new PayloadEnclosingImpl(payload); + + ObjectTemplate template = new ObjectTemplate(); + + ObjectAccessControls oacl = ObjectAccessControls.builder().bucket(BUCKET_NAME).entity("allUsers") + .role(ObjectRole.OWNER).build(); + + // This would trigger server side validation of md5 + hcMd5 = byteSource.hash(Hashing.md5()); + + // This would trigger server side validation of crc32c + hcCrc32c = byteSource.hash(Hashing.crc32c()); + + template.contentType("image/jpeg").addAcl(oacl).size(contentLength).name(MULTIPART_UPLOAD_OBJECT) + .contentLanguage("en").contentDisposition("attachment").md5Hash(hcMd5) + .customMetadata("custommetakey1", "custommetavalue1").crc32c(hcCrc32c) + .customMetadata(ImmutableMap.of("Adrian", "powderpuff")); + + GCSObject gcsObject = api().multipartUpload(BUCKET_NAME, template, payloadImpl.getPayload()); + + assertThat(gcsObject.getBucket()).isEqualTo(BUCKET_NAME); + assertThat(gcsObject.getName()).isEqualTo(MULTIPART_UPLOAD_OBJECT); + assertThat(gcsObject.getMd5HashCode()).isEqualTo(hcMd5); + assertThat(gcsObject.getCrc32cHashcode()).isEqualTo(hcCrc32c); + + assertThat(gcsObject.getAllMetadata()).contains(entry("custommetakey1", "custommetavalue1"), + entry("Adrian", "powderpuff")).doesNotContainKey("adrian"); + + PayloadEnclosingImpl impl = api().download(BUCKET_NAME, MULTIPART_UPLOAD_OBJECT); + + assertThat(ByteStreams2.toByteArrayAndClose(impl.getPayload().openStream())).isEqualTo( + ByteStreams2.toByteArrayAndClose(payloadImpl.getPayload().openStream())); + } + + @Test(groups = "live", dependsOnMethods = "testMultipartJpegUpload") public void testDeleteObject() { api().deleteObject(BUCKET_NAME2, UPLOAD_OBJECT_NAME); api().deleteObject(BUCKET_NAME2, COMPOSED_OBJECT2); api().deleteObject(BUCKET_NAME2, COMPOSED_OBJECT); api().deleteObject(BUCKET_NAME2, COPIED_OBJECT_NAME); + api().deleteObject(BUCKET_NAME, UPLOAD_OBJECT_NAME); api().deleteObject(BUCKET_NAME, UPLOAD_OBJECT_NAME2); + api().deleteObject(BUCKET_NAME, MULTIPART_UPLOAD_OBJECT); } @Test(groups = "live", dependsOnMethods = "testPatchObjectsWithOptions")
