| /* |
| * 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.blobstore.integration.internal; |
| |
| import static org.assertj.core.api.Fail.failBecauseExceptionWasNotThrown; |
| import static com.google.common.base.Charsets.UTF_8; |
| import static com.google.common.hash.Hashing.md5; |
| import static org.assertj.core.api.Assertions.assertThat; |
| import static org.jclouds.blobstore.options.GetOptions.Builder.ifETagDoesntMatch; |
| import static org.jclouds.blobstore.options.GetOptions.Builder.ifETagMatches; |
| import static org.jclouds.blobstore.options.GetOptions.Builder.ifModifiedSince; |
| import static org.jclouds.blobstore.options.GetOptions.Builder.ifUnmodifiedSince; |
| import static org.jclouds.blobstore.options.GetOptions.Builder.range; |
| import static org.jclouds.concurrent.FutureIterables.awaitCompletion; |
| import static org.jclouds.io.ByteStreams2.hashAndClose; |
| import static org.testng.Assert.assertEquals; |
| import static org.testng.Assert.assertNotNull; |
| import static org.testng.Assert.assertNull; |
| import static org.testng.Assert.fail; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.security.NoSuchAlgorithmException; |
| import java.security.cert.CertificateException; |
| import java.util.Date; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Random; |
| import java.util.concurrent.Callable; |
| import java.util.concurrent.ExecutionException; |
| import java.util.concurrent.TimeUnit; |
| |
| import javax.ws.rs.core.MediaType; |
| |
| import org.assertj.core.api.Fail; |
| import org.jclouds.blobstore.BlobStore; |
| import org.jclouds.blobstore.ContainerNotFoundException; |
| import org.jclouds.blobstore.KeyNotFoundException; |
| import org.jclouds.blobstore.domain.Blob; |
| import org.jclouds.blobstore.domain.BlobAccess; |
| import org.jclouds.blobstore.domain.BlobBuilder.PayloadBlobBuilder; |
| import org.jclouds.blobstore.domain.BlobMetadata; |
| import org.jclouds.blobstore.domain.MultipartPart; |
| import org.jclouds.blobstore.domain.MultipartUpload; |
| import org.jclouds.blobstore.domain.PageSet; |
| import org.jclouds.blobstore.domain.StorageMetadata; |
| import org.jclouds.blobstore.domain.StorageType; |
| import org.jclouds.blobstore.domain.Tier; |
| import org.jclouds.blobstore.options.CopyOptions; |
| import org.jclouds.blobstore.options.GetOptions; |
| import org.jclouds.blobstore.options.PutOptions; |
| import org.jclouds.blobstore.strategy.internal.MultipartUploadSlicingAlgorithm; |
| import org.jclouds.crypto.Crypto; |
| import org.jclouds.encryption.internal.JCECrypto; |
| import org.jclouds.http.HttpRequest; |
| import org.jclouds.http.HttpResponse; |
| import org.jclouds.http.HttpResponseException; |
| import org.jclouds.io.ByteStreams2; |
| import org.jclouds.io.ContentMetadataBuilder; |
| import org.jclouds.io.Payload; |
| import org.jclouds.io.Payloads; |
| import org.jclouds.io.payloads.ByteSourcePayload; |
| import org.jclouds.io.payloads.InputStreamPayload; |
| import org.jclouds.logging.Logger; |
| import org.jclouds.util.Closeables2; |
| import org.jclouds.utils.TestUtils; |
| import org.testng.ITestContext; |
| import org.testng.SkipException; |
| import org.testng.annotations.BeforeClass; |
| import org.testng.annotations.DataProvider; |
| import org.testng.annotations.Test; |
| |
| import com.google.common.base.Charsets; |
| import com.google.common.base.Predicate; |
| import com.google.common.base.Throwables; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableMap; |
| import com.google.common.collect.ImmutableSet; |
| import com.google.common.collect.Iterables; |
| import com.google.common.collect.Maps; |
| import com.google.common.hash.HashCode; |
| import com.google.common.hash.Hashing; |
| import com.google.common.io.ByteSource; |
| import com.google.common.io.ByteStreams; |
| import com.google.common.io.Files; |
| import com.google.common.net.HttpHeaders; |
| import com.google.common.util.concurrent.ListenableFuture; |
| |
| public class BaseBlobIntegrationTest extends BaseBlobStoreIntegrationTest { |
| private static final ByteSource oneHundredOneConstitutions = TestUtils.randomByteSource().slice(0, 101 * 45118); |
| |
| @BeforeClass(groups = { "integration", "live" }, dependsOnMethods = "setupContext") |
| @Override |
| public void setUpResourcesOnThisThread(ITestContext testContext) throws Exception { |
| super.setUpResourcesOnThisThread(testContext); |
| } |
| |
| public static ByteSource getTestDataSupplier() throws IOException { |
| return oneHundredOneConstitutions; |
| } |
| |
| /** |
| * Attempt to capture the issue detailed in |
| * http://groups.google.com/group/jclouds/browse_thread/thread/4a7c8d58530b287f |
| */ |
| @Test(groups = { "integration", "live" }) |
| public void testPutBlobParallel() throws Exception { |
| final ByteSource expected = createTestInput(32 * 1024); |
| |
| final String container = getContainerName(); |
| try { |
| Map<Integer, ListenableFuture<?>> responses = Maps.newHashMap(); |
| for (int i = 0; i < 3; i++) { |
| final String name = String.valueOf(i); |
| responses.put(i, this.exec.submit(new Callable<Void>() { |
| |
| @Override |
| public Void call() throws Exception { |
| Payload payload = Payloads.newByteSourcePayload(expected); |
| payload.getContentMetadata().setContentType("image/png"); |
| Blob blob = view.getBlobStore().blobBuilder(name).payload(payload).contentLength(expected.size()).build(); |
| view.getBlobStore().putBlob(container, blob); |
| |
| assertConsistencyAwareBlobExists(container, name); |
| |
| blob = view.getBlobStore().getBlob(container, name); |
| byte[] actual = ByteStreams2.toByteArrayAndClose(blob.getPayload().openStream()); |
| assertThat(actual).isEqualTo(expected.read()); |
| |
| view.getBlobStore().removeBlob(container, name); |
| assertConsistencyAwareBlobDoesntExist(container, name); |
| return null; |
| } |
| |
| })); |
| } |
| Map<Integer, Exception> exceptions = awaitCompletion(responses, exec, 30000L, Logger.CONSOLE, |
| "putFileParallel"); |
| assert exceptions.size() == 0 : exceptions; |
| |
| } finally { |
| returnContainer(container); |
| } |
| } |
| |
| @Test(groups = { "integration", "live" }) |
| public void testFileGetParallel() throws Exception { |
| final ByteSource supplier = createTestInput(32 * 1024); |
| final String container = getContainerName(); |
| try { |
| final String name = "constitution.txt"; |
| |
| uploadByteSource(container, name, supplier); |
| Map<Integer, ListenableFuture<?>> responses = Maps.newHashMap(); |
| for (int i = 0; i < 10; i++) { |
| responses.put(i, this.exec.submit(new Callable<Void>() { |
| @Override public Void call() throws Exception { |
| try { |
| Blob blob = view.getBlobStore().getBlob(container, name); |
| validateMetadata(blob.getMetadata(), container, name); |
| assertEquals(hashAndClose(blob.getPayload().openStream(), md5()), supplier.hash(md5())); |
| } catch (IOException e) { |
| Throwables.propagate(e); |
| } |
| return null; |
| } |
| })); |
| } |
| Map<Integer, Exception> exceptions = awaitCompletion(responses, exec, 30000L, Logger.CONSOLE, |
| "get constitution"); |
| if (!exceptions.isEmpty()) { |
| throw exceptions.values().iterator().next(); |
| } |
| |
| } finally { |
| returnContainer(container); |
| } |
| |
| } |
| |
| private void uploadByteSource(String container, String name, ByteSource byteSource) throws IOException { |
| BlobStore blobStore = view.getBlobStore(); |
| blobStore.putBlob(container, blobStore.blobBuilder(name) |
| .payload(new ByteSourcePayload(byteSource)) |
| .contentType("text/plain") |
| .contentMD5(byteSource.hash(md5())) |
| .contentLength(byteSource.size()) |
| .build()); |
| } |
| |
| @Test(groups = { "integration", "live" }) |
| public void testGetIfModifiedSince() throws InterruptedException { |
| String container = getContainerName(); |
| try { |
| String name = "apples"; |
| |
| Date before = new Date(System.currentTimeMillis() - 1000); |
| // first create the blob |
| addObjectAndValidateContent(container, name); |
| // now, modify it |
| addObjectAndValidateContent(container, name); |
| Date after = new Date(System.currentTimeMillis() + 1000); |
| |
| view.getBlobStore().getBlob(container, name, ifModifiedSince(before)); |
| validateContent(container, name); |
| |
| try { |
| view.getBlobStore().getBlob(container, name, ifModifiedSince(after)); |
| validateContent(container, name); |
| } catch (HttpResponseException ex) { |
| assertEquals(ex.getResponse().getStatusCode(), 304); |
| } |
| } finally { |
| returnContainer(container); |
| } |
| |
| } |
| |
| @Test(groups = { "integration", "live" }) |
| public void testOverwriteBlob() throws InterruptedException { |
| String container = getContainerName(); |
| BlobStore blobStore = view.getBlobStore(); |
| try { |
| String blobName = "hello"; |
| Blob blob = blobStore.blobBuilder(blobName) |
| .payload(TEST_STRING) |
| .build(); |
| blobStore.putBlob(container, blob); |
| Blob overwriteBlob = blobStore.blobBuilder(blobName) |
| .payload("overwrite content") |
| .build(); |
| blobStore.putBlob(container, overwriteBlob); |
| } finally { |
| returnContainer(container); |
| } |
| } |
| |
| @Test(groups = { "integration", "live" }) |
| public void testCreateBlobWithExpiry() throws InterruptedException { |
| String container = getContainerName(); |
| BlobStore blobStore = view.getBlobStore(); |
| try { |
| final String blobName = "hello"; |
| final Date expires = new Date((System.currentTimeMillis() / 1000) * 1000 + 60 * 1000); |
| |
| blobStore.putBlob(container, blobStore.blobBuilder(blobName).payload(TEST_STRING).expires(expires).build()); |
| |
| assertConsistencyAwareBlobExpiryMetadata(container, blobName, expires); |
| |
| } finally { |
| returnContainer(container); |
| } |
| } |
| |
| private void putBlobWithMd5(Payload payload, long contentLength, HashCode contentMD5) throws InterruptedException, IOException { |
| String container = getContainerName(); |
| BlobStore blobStore = view.getBlobStore(); |
| try { |
| String blobName = "putBlobWithMd5-" + new Random().nextLong(); |
| Blob blob = blobStore |
| .blobBuilder(blobName) |
| .payload(payload) |
| .contentLength(contentLength) |
| .contentMD5(contentMD5) |
| .build(); |
| blobStore.putBlob(container, blob); |
| } finally { |
| returnContainer(container); |
| } |
| } |
| |
| protected int getIncorrectContentMD5StatusCode() { |
| return 400; |
| } |
| |
| @Test(groups = { "integration", "live" }) |
| public void testPutCorrectContentMD5ByteSource() throws InterruptedException, IOException { |
| ByteSource payload = createTestInput(1024); |
| HashCode contentMD5 = md5().hashBytes(payload.read()); |
| putBlobWithMd5(new ByteSourcePayload(payload), payload.size(), contentMD5); |
| } |
| |
| @Test(groups = { "integration", "live" }) |
| public void testPutIncorrectContentMD5ByteSource() throws InterruptedException, IOException { |
| ByteSource payload = createTestInput(1024); |
| HashCode contentMD5 = md5().hashBytes(new byte[0]); |
| try { |
| putBlobWithMd5(new ByteSourcePayload(payload), payload.size(), contentMD5); |
| fail(); |
| } catch (HttpResponseException hre) { |
| if (hre.getResponse().getStatusCode() != getIncorrectContentMD5StatusCode()) { |
| throw hre; |
| } |
| } |
| } |
| |
| @Test(groups = { "integration", "live" }) |
| public void testPutCorrectContentMD5InputStream() throws InterruptedException, IOException { |
| ByteSource payload = createTestInput(1024); |
| HashCode contentMD5 = md5().hashBytes(payload.read()); |
| putBlobWithMd5(new InputStreamPayload(payload.openStream()), payload.size(), contentMD5); |
| } |
| |
| @Test(groups = { "integration", "live" }) |
| public void testPutIncorrectContentMD5InputStream() throws InterruptedException, IOException { |
| ByteSource payload = createTestInput(1024); |
| HashCode contentMD5 = md5().hashBytes(new byte[0]); |
| try { |
| putBlobWithMd5(new InputStreamPayload(payload.openStream()), payload.size(), contentMD5); |
| fail(); |
| } catch (HttpResponseException hre) { |
| if (hre.getResponse().getStatusCode() != getIncorrectContentMD5StatusCode()) { |
| throw hre; |
| } |
| } |
| } |
| |
| @Test(groups = { "integration", "live" }) |
| public void testGetIfUnmodifiedSince() throws InterruptedException { |
| String container = getContainerName(); |
| try { |
| |
| String name = "apples"; |
| |
| Date before = new Date(System.currentTimeMillis() - 10000); |
| addObjectAndValidateContent(container, name); |
| Date after = new Date(System.currentTimeMillis() + 10000); |
| |
| awaitConsistency(); |
| view.getBlobStore().getBlob(container, name, ifUnmodifiedSince(after)); |
| validateContent(container, name); |
| |
| try { |
| view.getBlobStore().getBlob(container, name, ifUnmodifiedSince(before)); |
| validateContent(container, name); |
| } catch (HttpResponseException ex) { |
| assertEquals(ex.getResponse().getStatusCode(), 412); |
| } |
| } finally { |
| returnContainer(container); |
| } |
| } |
| |
| @Test(groups = { "integration", "live" }) |
| public void testGetIfMatch() throws InterruptedException { |
| String container = getContainerName(); |
| try { |
| |
| String name = "apples"; |
| |
| String goodETag = addObjectAndValidateContent(container, name); |
| |
| view.getBlobStore().getBlob(container, name, ifETagMatches(goodETag)); |
| validateContent(container, name); |
| |
| try { |
| view.getBlobStore().getBlob(container, name, ifETagMatches("powerfrisbee")); |
| validateContent(container, name); |
| } catch (HttpResponseException ex) { |
| assertEquals(ex.getResponse().getStatusCode(), 412); |
| } |
| } finally { |
| returnContainer(container); |
| } |
| } |
| |
| @Test(groups = { "integration", "live" }) |
| public void testGetIfNoneMatch() throws InterruptedException { |
| String container = getContainerName(); |
| try { |
| |
| String name = "apples"; |
| |
| String goodETag = addObjectAndValidateContent(container, name); |
| |
| view.getBlobStore().getBlob(container, name, ifETagDoesntMatch("powerfrisbee")); |
| validateContent(container, name); |
| |
| try { |
| view.getBlobStore().getBlob(container, name, ifETagDoesntMatch(goodETag)); |
| } catch (HttpResponseException ex) { |
| assertEquals(ex.getResponse().getStatusCode(), 304); |
| } |
| } finally { |
| returnContainer(container); |
| } |
| } |
| |
| @Test(groups = { "integration", "live" }) |
| public void testGetRangeOutOfRange() throws InterruptedException, IOException { |
| String container = getContainerName(); |
| try { |
| String name = "apples"; |
| |
| addObjectAndValidateContent(container, name); |
| try { |
| view.getBlobStore().getBlob(container, name, range(TEST_STRING.length(), TEST_STRING.length() + 1)); |
| failBecauseExceptionWasNotThrown(HttpResponseException.class); |
| } catch (HttpResponseException e) { |
| assertThat(e.getResponse().getStatusCode()).isEqualTo(416); |
| } |
| } finally { |
| returnContainer(container); |
| } |
| |
| } |
| |
| @Test(groups = { "integration", "live" }) |
| public void testGetRange() throws InterruptedException, IOException { |
| String container = getContainerName(); |
| try { |
| |
| String name = "apples"; |
| |
| addObjectAndValidateContent(container, name); |
| Blob blob1 = view.getBlobStore().getBlob(container, name, range(0, 5)); |
| validateMetadata(blob1.getMetadata(), container, name); |
| assertEquals(getContentAsStringOrNullAndClose(blob1), TEST_STRING.substring(0, 6)); |
| assertThat(blob1.getAllHeaders().get(HttpHeaders.CONTENT_RANGE)).containsExactly("bytes 0-5/46"); |
| |
| Blob blob2 = view.getBlobStore().getBlob(container, name, range(6, TEST_STRING.length())); |
| validateMetadata(blob2.getMetadata(), container, name); |
| assertEquals(getContentAsStringOrNullAndClose(blob2), TEST_STRING.substring(6, TEST_STRING.length())); |
| assertThat(blob2.getAllHeaders().get(HttpHeaders.CONTENT_RANGE)).containsExactly("bytes 6-45/46"); |
| |
| /* RFC 2616 14.35.1 |
| "If the entity is shorter than the specified suffix-length, the |
| entire entity-body is used." */ |
| Blob blob3 = view.getBlobStore().getBlob(container, name, new GetOptions().tail(TEST_STRING.length() + 10)); |
| validateMetadata(blob3.getMetadata(), container, name); |
| assertEquals(getContentAsStringOrNullAndClose(blob3), TEST_STRING); |
| // not all providers return Content-Range for non-partial responses |
| } finally { |
| returnContainer(container); |
| } |
| } |
| |
| @Test(groups = { "integration", "live" }) |
| public void testGetTwoRanges() throws InterruptedException, IOException { |
| String container = getContainerName(); |
| try { |
| |
| String name = "apples"; |
| |
| addObjectAndValidateContent(container, name); |
| Blob blob = view.getBlobStore().getBlob(container, name, range(0, 5).range(6, TEST_STRING.length())); |
| validateMetadata(blob.getMetadata(), container, name); |
| assertEquals(getContentAsStringOrNullAndClose(blob), TEST_STRING); |
| } finally { |
| returnContainer(container); |
| } |
| } |
| |
| @Test(groups = { "integration", "live" }) |
| public void testGetRangeMultipart() throws InterruptedException, IOException { |
| String container = getContainerName(); |
| InputStream expect = null; |
| InputStream actual = null; |
| try { |
| String name = "apples"; |
| long length = getMinimumMultipartBlobSize(); |
| ByteSource byteSource = TestUtils.randomByteSource().slice(0, length); |
| Blob blob = view.getBlobStore().blobBuilder(name) |
| .payload(byteSource) |
| .contentLength(length) |
| .build(); |
| view.getBlobStore().putBlob(container, blob, new PutOptions().multipart(true)); |
| blob = view.getBlobStore().getBlob(container, name, range(0, 5)); |
| validateMetadata(blob.getMetadata(), container, name); |
| expect = byteSource.slice(0, 6).openStream(); |
| actual = blob.getPayload().openStream(); |
| assertThat(actual).hasContentEqualTo(expect); |
| } finally { |
| Closeables2.closeQuietly(expect); |
| Closeables2.closeQuietly(actual); |
| returnContainer(container); |
| } |
| } |
| |
| private String addObjectAndValidateContent(String sourcecontainer, String sourceKey) throws InterruptedException { |
| String eTag = addBlobToContainer(sourcecontainer, sourceKey); |
| validateContent(sourcecontainer, sourceKey); |
| awaitConsistency(); |
| return eTag; |
| } |
| |
| @Test(groups = { "integration", "live" }) |
| public void deleteObjectNotFound() throws InterruptedException { |
| String container = getContainerName(); |
| String name = "test"; |
| try { |
| view.getBlobStore().removeBlob(container, name); |
| } finally { |
| returnContainer(container); |
| } |
| } |
| |
| @Test(groups = { "integration", "live" }) |
| public void blobNotFound() throws InterruptedException { |
| String container = getContainerName(); |
| String name = "test"; |
| try { |
| assert !view.getBlobStore().blobExists(container, name); |
| } finally { |
| returnContainer(container); |
| } |
| } |
| |
| @DataProvider(name = "delete") |
| public Object[][] createData() { |
| if (System.getProperty("os.name").toLowerCase().contains("windows")) { |
| return new Object[][] { { "normal" }, { "sp ace" } }; |
| } else { |
| return new Object[][] { { "normal" }, { "sp ace" }, { "qu?stion" }, { "unic₪de" }, { "path/foo" }, { "colon:" }, |
| { "asteri*k" }, { "quote\"" }, { "{great<r}" }, { "lesst>en" }, { "p|pe" } }; |
| } |
| } |
| |
| @Test(groups = { "integration", "live" }, dataProvider = "delete") |
| public void deleteObject(String name) throws InterruptedException { |
| String container = getContainerName(); |
| try { |
| addBlobToContainer(container, name, name, MediaType.TEXT_PLAIN); |
| awaitConsistency(); |
| view.getBlobStore().removeBlob(container, name); |
| awaitConsistency(); |
| assertContainerEmptyDeleting(container, name); |
| } finally { |
| returnContainer(container); |
| } |
| } |
| |
| private void assertContainerEmptyDeleting(String container, String name) { |
| Iterable<? extends StorageMetadata> listing = Iterables.filter(view.getBlobStore().list(container), |
| new Predicate<StorageMetadata>() { |
| |
| @Override |
| public boolean apply(StorageMetadata input) { |
| return input.getType() == StorageType.BLOB; |
| } |
| |
| }); |
| assertEquals(Iterables.size(listing), 0, String.format( |
| "deleting %s, we still have %s blobs left in container %s, using encoding %s", name, Iterables |
| .size(listing), container, LOCAL_ENCODING)); |
| } |
| |
| @Test(groups = { "integration", "live" }) |
| public void deleteObjectNoContainer() { |
| try { |
| view.getBlobStore().removeBlob("donb", "test"); |
| } catch (HttpResponseException e) { |
| assertEquals(e.getResponse().getStatusCode(), 404); |
| } catch (ContainerNotFoundException e) { |
| } |
| |
| } |
| |
| @Test(groups = { "integration", "live" }, dataProvider = "delete") |
| public void deleteMultipleObjects(String name) throws InterruptedException { |
| String name2 = name + "2"; |
| String container = getContainerName(); |
| try { |
| addBlobToContainer(container, name, name, MediaType.TEXT_PLAIN); |
| addBlobToContainer(container, name2, name2, MediaType.TEXT_PLAIN); |
| awaitConsistency(); |
| view.getBlobStore().removeBlobs(container, ImmutableSet.of(name, name2)); |
| awaitConsistency(); |
| assertContainerEmptyDeleting(container, name); |
| } finally { |
| returnContainer(container); |
| } |
| } |
| |
| @DataProvider(name = "putTests") |
| public Object[][] createData1() throws IOException { |
| File file = new File("pom.xml"); |
| String realObject = Files.toString(file, Charsets.UTF_8); |
| |
| return new Object[][] { { "file", "text/xml", file, realObject }, |
| { "string", "text/xml", realObject, realObject }, |
| { "bytes", "application/octet-stream", realObject.getBytes(), realObject } }; |
| } |
| |
| @Test(groups = { "integration", "live" }, dataProvider = "putTests") |
| public void testPutObject(String name, String type, Object content, Object realObject) throws InterruptedException, |
| IOException { |
| PayloadBlobBuilder blobBuilder = view.getBlobStore().blobBuilder(name).payload(Payloads.newPayload(content)) |
| .contentType(type); |
| addContentMetadata(blobBuilder); |
| Blob blob = blobBuilder.build(); |
| String container = getContainerName(); |
| try { |
| assertNotNull(view.getBlobStore().putBlob(container, blob)); |
| awaitConsistency(); |
| |
| blob = view.getBlobStore().getBlob(container, blob.getMetadata().getName()); |
| validateMetadata(blob.getMetadata(), container, name); |
| checkContentMetadata(blob); |
| |
| String returnedString = getContentAsStringOrNullAndClose(blob); |
| assertEquals(returnedString, realObject); |
| PageSet<? extends StorageMetadata> set = view.getBlobStore().list(container); |
| assert set.size() == 1 : set; |
| } finally { |
| returnContainer(container); |
| } |
| } |
| |
| @Test(groups = { "integration", "live" }) |
| public void testPutObjectStream() throws InterruptedException, IOException, ExecutionException { |
| PayloadBlobBuilder blobBuilder = view.getBlobStore().blobBuilder("streaming").payload( |
| new ByteSourcePayload(ByteSource.wrap("foo".getBytes()))); |
| addContentMetadata(blobBuilder); |
| |
| Blob blob = blobBuilder.build(); |
| |
| String container = getContainerName(); |
| try { |
| |
| assertNotNull(view.getBlobStore().putBlob(container, blob)); |
| awaitConsistency(); |
| |
| blob = view.getBlobStore().getBlob(container, blob.getMetadata().getName()); |
| String returnedString = getContentAsStringOrNullAndClose(blob); |
| assertEquals(returnedString, "foo"); |
| validateMetadata(blob.getMetadata(), container, blob.getMetadata().getName()); |
| checkContentMetadata(blob); |
| PageSet<? extends StorageMetadata> set = view.getBlobStore().list(container); |
| assert set.size() == 1 : set; |
| } finally { |
| returnContainer(container); |
| } |
| } |
| |
| @Test(groups = { "integration", "live" }) |
| public void testPutByteSource() throws Exception { |
| long length = 42; |
| ByteSource byteSource = TestUtils.randomByteSource().slice(0, length); |
| Payload payload = new ByteSourcePayload(byteSource); |
| testPut(payload, null, payload, length, new PutOptions()); |
| } |
| |
| @Test(groups = { "integration", "live" }) |
| public void testPutInputStream() throws Exception { |
| long length = 42; |
| ByteSource byteSource = TestUtils.randomByteSource().slice(0, length); |
| Payload payload = new InputStreamPayload(byteSource.openStream()); |
| testPut(payload, null, new ByteSourcePayload(byteSource), length, new PutOptions()); |
| } |
| |
| @Test(groups = { "integration", "live" }) |
| public void testPutZeroLengthByteSource() throws Exception { |
| long length = 0; |
| // do not use ByteSource.empty() since it is backed by a |
| // ByteArrayInputStream which supports reset |
| ByteSource byteSource = TestUtils.randomByteSource().slice(0, length); |
| Payload payload = new ByteSourcePayload(byteSource); |
| testPut(payload, null, payload, length, new PutOptions()); |
| } |
| |
| @Test(groups = { "integration", "live" }) |
| public void testPutZeroLengthInputStream() throws Exception { |
| long length = 0; |
| // do not use ByteSource.empty() since it is backed by a |
| // ByteArrayInputStream which supports reset |
| ByteSource byteSource = TestUtils.randomByteSource().slice(0, length); |
| Payload payload = new InputStreamPayload(byteSource.openStream()); |
| testPut(payload, null, payload, length, new PutOptions()); |
| } |
| |
| @Test(groups = { "integration", "live" }) |
| public void testPutMultipartByteSource() throws Exception { |
| long length = Math.max(getMinimumMultipartBlobSize(), MultipartUploadSlicingAlgorithm.DEFAULT_PART_SIZE + 1); |
| BlobStore blobStore = view.getBlobStore(); |
| MultipartUploadSlicingAlgorithm algorithm = new MultipartUploadSlicingAlgorithm( |
| blobStore.getMinimumMultipartPartSize(), blobStore.getMaximumMultipartPartSize(), |
| blobStore.getMaximumNumberOfParts()); |
| // make sure that we are creating multiple parts |
| assertThat(algorithm.calculateChunkSize(length)).isLessThan(length); |
| ByteSource byteSource = TestUtils.randomByteSource().slice(0, length); |
| Payload payload = new ByteSourcePayload(byteSource); |
| HashCode hashCode = byteSource.hash(Hashing.md5()); |
| testPut(payload, hashCode, payload, length, new PutOptions().multipart(true)); |
| } |
| |
| @Test(groups = { "integration", "live" }) |
| public void testPutMultipartInputStream() throws Exception { |
| long length = Math.max(getMinimumMultipartBlobSize(), MultipartUploadSlicingAlgorithm.DEFAULT_PART_SIZE + 1); |
| BlobStore blobStore = view.getBlobStore(); |
| MultipartUploadSlicingAlgorithm algorithm = new MultipartUploadSlicingAlgorithm( |
| blobStore.getMinimumMultipartPartSize(), blobStore.getMaximumMultipartPartSize(), |
| blobStore.getMaximumNumberOfParts()); |
| // make sure that we are creating multiple parts |
| assertThat(algorithm.calculateChunkSize(length)).isLessThan(length); |
| ByteSource byteSource = TestUtils.randomByteSource().slice(0, length); |
| Payload payload = new InputStreamPayload(byteSource.openStream()); |
| testPut(payload, null, new ByteSourcePayload(byteSource), length, new PutOptions().multipart(true)); |
| } |
| |
| @Test(groups = { "integration", "live" }) |
| public void testSetBlobAccess() throws Exception { |
| BlobStore blobStore = view.getBlobStore(); |
| String containerName = getContainerName(); |
| String blobName = "set-access-blob-name"; |
| try { |
| addBlobToContainer(containerName, blobName, blobName, MediaType.TEXT_PLAIN); |
| |
| assertThat(blobStore.getBlobAccess(containerName, blobName)).isEqualTo(BlobAccess.PRIVATE); |
| |
| blobStore.setBlobAccess(containerName, blobName, BlobAccess.PUBLIC_READ); |
| assertThat(blobStore.getBlobAccess(containerName, blobName)).isEqualTo(BlobAccess.PUBLIC_READ); |
| |
| // test that blob is anonymously readable |
| HttpRequest request = view.getSigner().signGetBlob(containerName, blobName).toBuilder() |
| .replaceQueryParams(ImmutableMap.<String, String>of()).build(); |
| HttpResponse response = view.utils().http().invoke(request); |
| assertThat(response.getStatusCode()).isEqualTo(200); |
| |
| blobStore.setBlobAccess(containerName, blobName, BlobAccess.PRIVATE); |
| assertThat(blobStore.getBlobAccess(containerName, blobName)).isEqualTo(BlobAccess.PRIVATE); |
| } finally { |
| returnContainer(containerName); |
| } |
| } |
| |
| @Test(groups = { "integration", "live" }) |
| public void testPutBlobAccess() throws Exception { |
| BlobStore blobStore = view.getBlobStore(); |
| String containerName = getContainerName(); |
| try { |
| String blobNamePrivate = "put-access-blob-name-private"; |
| Blob blobPrivate = blobStore.blobBuilder(blobNamePrivate).payload(new byte[1]).build(); |
| blobStore.putBlob(containerName, blobPrivate, new PutOptions().setBlobAccess(BlobAccess.PRIVATE)); |
| assertThat(blobStore.getBlobAccess(containerName, blobNamePrivate)).isEqualTo(BlobAccess.PRIVATE); |
| |
| String blobNamePublic = "put-access-blob-name-public"; |
| Blob blobPublic = blobStore.blobBuilder(blobNamePublic).payload(new byte[1]).build(); |
| blobStore.putBlob(containerName, blobPublic, new PutOptions().setBlobAccess(BlobAccess.PUBLIC_READ)); |
| assertThat(blobStore.getBlobAccess(containerName, blobNamePublic)).isEqualTo(BlobAccess.PUBLIC_READ); |
| } finally { |
| returnContainer(containerName); |
| } |
| } |
| |
| @Test(groups = { "integration", "live" }) |
| public void testPutBlobAccessMultipart() throws Exception { |
| BlobStore blobStore = view.getBlobStore(); |
| String containerName = getContainerName(); |
| ByteSource byteSource = TestUtils.randomByteSource().slice(0, getMinimumMultipartBlobSize()); |
| Payload payload = Payloads.newByteSourcePayload(byteSource); |
| payload.getContentMetadata().setContentLength(byteSource.size()); |
| try { |
| String blobNamePrivate = "put-access-blob-name-private"; |
| Blob blobPrivate = blobStore.blobBuilder(blobNamePrivate).payload(payload).build(); |
| blobStore.putBlob(containerName, blobPrivate, new PutOptions().setBlobAccess(BlobAccess.PRIVATE).multipart(true)); |
| assertThat(blobStore.getBlobAccess(containerName, blobNamePrivate)).isEqualTo(BlobAccess.PRIVATE); |
| |
| String blobNamePublic = "put-access-blob-name-public"; |
| Blob blobPublic = blobStore.blobBuilder(blobNamePublic).payload(payload).build(); |
| blobStore.putBlob(containerName, blobPublic, new PutOptions().setBlobAccess(BlobAccess.PUBLIC_READ).multipart(true)); |
| assertThat(blobStore.getBlobAccess(containerName, blobNamePublic)).isEqualTo(BlobAccess.PUBLIC_READ); |
| } finally { |
| returnContainer(containerName); |
| } |
| } |
| |
| @Test(groups = { "integration", "live" }) |
| public void testPutBlobTierStandard() throws Exception { |
| testPutBlobTierHelper(Tier.STANDARD, new PutOptions()); |
| } |
| |
| @Test(groups = { "integration", "live" }) |
| public void testPutBlobTierInfrequent() throws Exception { |
| testPutBlobTierHelper(Tier.INFREQUENT, new PutOptions()); |
| } |
| |
| @Test(groups = { "integration", "live" }) |
| public void testPutBlobTierArchive() throws Exception { |
| testPutBlobTierHelper(Tier.ARCHIVE, new PutOptions()); |
| } |
| |
| @Test(groups = { "integration", "live" }) |
| public void testPutBlobTierStandardMultipart() throws Exception { |
| testPutBlobTierHelper(Tier.STANDARD, new PutOptions().multipart(true)); |
| } |
| |
| @Test(groups = { "integration", "live" }) |
| public void testPutBlobTierInfrequentMultipart() throws Exception { |
| testPutBlobTierHelper(Tier.INFREQUENT, new PutOptions().multipart(true)); |
| } |
| |
| @Test(groups = { "integration", "live" }) |
| public void testPutBlobTierArchiveMultipart() throws Exception { |
| testPutBlobTierHelper(Tier.ARCHIVE, new PutOptions().multipart(true)); |
| } |
| |
| protected void testPutBlobTierHelper(Tier tier, PutOptions options) throws Exception { |
| String blobName = "put-blob-tier-" + tier; |
| ByteSource payload = createTestInput(1024); |
| BlobStore blobStore = view.getBlobStore(); |
| String containerName = getContainerName(); |
| try { |
| Blob blob = blobStore.blobBuilder(blobName) |
| .payload(payload) |
| .contentLength(payload.size()) |
| .tier(tier) |
| .build(); |
| blobStore.putBlob(containerName, blob, options); |
| checkTier(blobStore.blobMetadata(containerName, blobName), tier); |
| } finally { |
| returnContainer(containerName); |
| } |
| } |
| |
| protected void checkTier(BlobMetadata metadata, Tier expected) { |
| assertThat(metadata.getTier()).isEqualTo(expected); |
| } |
| |
| protected void checkUserMetadata(Map<String, String> userMetadata1, Map<String, String> userMetadata2) { |
| assertThat(userMetadata1).isEqualTo(userMetadata2); |
| } |
| |
| private void testPut(Payload payload, HashCode hashCode, Payload expectedPayload, long length, PutOptions options) |
| throws IOException, InterruptedException { |
| BlobStore blobStore = view.getBlobStore(); |
| String blobName = "multipart-upload"; |
| Map<String, String> userMetadata = ImmutableMap.of("key1", "value1", "key2", "value2"); |
| PayloadBlobBuilder blobBuilder = blobStore.blobBuilder(blobName) |
| .userMetadata(userMetadata) |
| .payload(payload) |
| .contentLength(length); |
| addContentMetadata(blobBuilder); |
| if (hashCode != null) { |
| blobBuilder.contentMD5(payload.getContentMetadata().getContentMD5AsHashCode()); |
| } |
| |
| String container = getContainerName(); |
| try { |
| String etag = blobStore.putBlob(container, blobBuilder.build(), options); |
| assertThat(etag).isNotNull(); |
| |
| Blob blob = blobStore.getBlob(container, blobName); |
| assertThat(blob.getMetadata().getContentMetadata().getContentLength()).isEqualTo(length); |
| |
| InputStream is = null; |
| try { |
| is = blob.getPayload().openStream(); |
| assertThat(is).hasContentEqualTo(expectedPayload.openStream()); |
| } finally { |
| Closeables2.closeQuietly(is); |
| } |
| validateMetadata(blob.getMetadata(), container, blob.getMetadata().getName()); |
| checkContentMetadata(blob); |
| checkUserMetadata(blob.getMetadata().getUserMetadata(), userMetadata); |
| } finally { |
| returnContainer(container); |
| } |
| } |
| |
| protected long getMinimumMultipartBlobSize() { |
| throw new SkipException("multipart upload not supported"); |
| } |
| |
| protected void checkContentMetadata(Blob blob) { |
| checkCacheControl(blob, "max-age=3600"); |
| checkContentType(blob, "text/csv"); |
| checkContentDisposition(blob, "attachment; filename=photo.jpg"); |
| checkContentEncoding(blob, "gzip"); |
| checkContentLanguage(blob, "en"); |
| } |
| |
| protected void addContentMetadata(PayloadBlobBuilder blobBuilder) { |
| blobBuilder.cacheControl("max-age=3600"); |
| blobBuilder.contentType("text/csv"); |
| blobBuilder.contentDisposition("attachment; filename=photo.jpg"); |
| blobBuilder.contentEncoding("gzip"); |
| blobBuilder.contentLanguage("en"); |
| } |
| |
| protected void checkCacheControl(Blob blob, String cacheControl) { |
| assertThat(blob.getPayload().getContentMetadata().getCacheControl()).isEqualTo(cacheControl); |
| assertThat(blob.getMetadata().getContentMetadata().getCacheControl()).isEqualTo(cacheControl); |
| } |
| |
| protected void checkContentType(Blob blob, String contentType) { |
| assert blob.getPayload().getContentMetadata().getContentType().startsWith(contentType) : blob.getPayload() |
| .getContentMetadata().getContentType(); |
| assert blob.getMetadata().getContentMetadata().getContentType().startsWith(contentType) : blob.getMetadata() |
| .getContentMetadata().getContentType(); |
| } |
| |
| protected void checkContentDisposition(Blob blob, String contentDisposition) { |
| assert blob.getPayload().getContentMetadata().getContentDisposition().startsWith(contentDisposition) : blob |
| .getPayload().getContentMetadata().getContentDisposition(); |
| assert blob.getMetadata().getContentMetadata().getContentDisposition().startsWith(contentDisposition) : blob |
| .getMetadata().getContentMetadata().getContentDisposition(); |
| |
| } |
| |
| protected void checkContentEncoding(Blob blob, String contentEncoding) { |
| assert blob.getPayload().getContentMetadata().getContentEncoding().indexOf(contentEncoding) != -1 : blob |
| .getPayload().getContentMetadata().getContentEncoding(); |
| assert blob.getMetadata().getContentMetadata().getContentEncoding().indexOf(contentEncoding) != -1 : blob |
| .getMetadata().getContentMetadata().getContentEncoding(); |
| } |
| |
| protected void checkContentLanguage(Blob blob, String contentLanguage) { |
| assert blob.getPayload().getContentMetadata().getContentLanguage().startsWith(contentLanguage) : blob |
| .getPayload().getContentMetadata().getContentLanguage(); |
| assert blob.getMetadata().getContentMetadata().getContentLanguage().startsWith(contentLanguage) : blob |
| .getMetadata().getContentMetadata().getContentLanguage(); |
| } |
| |
| protected void checkMPUParts(Blob newBlob, List<MultipartPart> parts) { |
| } |
| |
| protected static volatile Crypto crypto; |
| static { |
| try { |
| crypto = new JCECrypto(); |
| } catch (NoSuchAlgorithmException e) { |
| Throwables.propagate(e); |
| } catch (CertificateException e) { |
| Throwables.propagate(e); |
| } |
| } |
| |
| @Test(groups = { "integration", "live" }) |
| public void testMetadata() throws InterruptedException, IOException { |
| String name = "hello"; |
| // NOTE all metadata in jclouds comes out as lowercase, in an effort to |
| // normalize the |
| // providers. |
| Blob blob = view.getBlobStore().blobBuilder(name).userMetadata(ImmutableMap.of("Adrian", "powderpuff")) |
| .payload(TEST_STRING).contentType(MediaType.TEXT_PLAIN) |
| .contentMD5(md5().hashString(TEST_STRING, Charsets.UTF_8).asBytes()) |
| .build(); |
| String container = getContainerName(); |
| try { |
| assertNull(view.getBlobStore().blobMetadata(container, "powderpuff")); |
| |
| addBlobToContainer(container, blob); |
| Blob newObject = validateContent(container, name); |
| |
| BlobMetadata metadata = newObject.getMetadata(); |
| |
| validateMetadata(metadata); |
| validateMetadata(metadata, container, name); |
| validateMetadata(view.getBlobStore().blobMetadata(container, name)); |
| |
| // write 2 items with the same name to ensure that provider doesn't |
| // accept dupes |
| blob.getMetadata().getUserMetadata().put("Adrian", "wonderpuff"); |
| blob.getMetadata().getUserMetadata().put("Adrian", "powderpuff"); |
| |
| awaitConsistency(); |
| addBlobToContainer(container, blob); |
| awaitConsistency(); |
| validateMetadata(view.getBlobStore().blobMetadata(container, name)); |
| |
| } finally { |
| returnContainer(container); |
| } |
| } |
| |
| @Test(groups = { "integration", "live" }) |
| public void testCopyBlobCopyMetadata() throws Exception { |
| BlobStore blobStore = view.getBlobStore(); |
| String fromName = "source"; |
| String toName = "to"; |
| ByteSource payload = TestUtils.randomByteSource().slice(0, 1024); |
| Map<String, String> userMetadata = ImmutableMap.of("key1", "value1", "key2", "value2"); |
| PayloadBlobBuilder blobBuilder = blobStore |
| .blobBuilder(fromName) |
| .payload(payload) |
| .contentLength(payload.size()); |
| addContentMetadata(blobBuilder); |
| blobBuilder.userMetadata(userMetadata); |
| Blob blob = blobBuilder.build(); |
| |
| String fromContainer = getContainerName(); |
| String toContainer = getContainerName(); |
| try { |
| blobStore.putBlob(fromContainer, blob); |
| blobStore.copyBlob(fromContainer, fromName, toContainer, toName, CopyOptions.NONE); |
| Blob toBlob = blobStore.getBlob(toContainer, toName); |
| InputStream is = null; |
| try { |
| is = toBlob.getPayload().openStream(); |
| assertEquals(ByteStreams.toByteArray(is), payload.read()); |
| } finally { |
| Closeables2.closeQuietly(is); |
| } |
| checkContentMetadata(toBlob); |
| checkUserMetadata(toBlob.getMetadata().getUserMetadata(), userMetadata); |
| } finally { |
| returnContainer(toContainer); |
| returnContainer(fromContainer); |
| } |
| } |
| |
| @Test(groups = { "integration", "live" }) |
| public void testCopyBlobReplaceMetadata() throws Exception { |
| BlobStore blobStore = view.getBlobStore(); |
| String fromName = "source"; |
| String toName = "to"; |
| ByteSource payload = TestUtils.randomByteSource().slice(0, 1024); |
| PayloadBlobBuilder blobBuilder = blobStore |
| .blobBuilder(fromName) |
| .userMetadata(ImmutableMap.of("key1", "value1", "key2", "value2")) |
| .payload(payload) |
| .cacheControl("max-age=1800") |
| .contentLength(payload.size()) |
| .contentDisposition("attachment; filename=original.jpg") |
| .contentEncoding("compress") |
| .contentLanguage("fr") |
| .contentType("audio/ogg"); |
| Blob blob = blobBuilder.build(); |
| |
| String fromContainer = getContainerName(); |
| String toContainer = getContainerName(); |
| try { |
| blobStore.putBlob(fromContainer, blob); |
| Map<String, String> userMetadata = ImmutableMap.of("key3", "value3", "key4", "value4"); |
| blobStore.copyBlob(fromContainer, fromName, toContainer, toName, CopyOptions.builder() |
| .contentMetadata(ContentMetadataBuilder.create() |
| .cacheControl("max-age=3600") |
| .contentType("text/csv") |
| .contentDisposition("attachment; filename=photo.jpg") |
| .contentEncoding("gzip") |
| .contentLanguage("en") |
| .build()) |
| .userMetadata(userMetadata) |
| .build()); |
| Blob toBlob = blobStore.getBlob(toContainer, toName); |
| InputStream is = null; |
| try { |
| is = toBlob.getPayload().openStream(); |
| assertEquals(ByteStreams.toByteArray(is), payload.read()); |
| } finally { |
| Closeables2.closeQuietly(is); |
| } |
| checkContentMetadata(toBlob); |
| checkUserMetadata(toBlob.getMetadata().getUserMetadata(), userMetadata); |
| } finally { |
| returnContainer(toContainer); |
| returnContainer(fromContainer); |
| } |
| } |
| |
| @Test(groups = { "integration", "live" }) |
| public void testCopyIfMatch() throws Exception { |
| BlobStore blobStore = view.getBlobStore(); |
| String fromName = "source"; |
| String toName = "to"; |
| ByteSource payload = TestUtils.randomByteSource().slice(0, 1024); |
| Blob blob = blobStore |
| .blobBuilder(fromName) |
| .payload(payload) |
| .contentLength(payload.size()) |
| .build(); |
| String fromContainer = getContainerName(); |
| String toContainer = getContainerName(); |
| try { |
| String eTag = blobStore.putBlob(fromContainer, blob); |
| blobStore.copyBlob(fromContainer, fromName, toContainer, toName, CopyOptions.builder().ifMatch(eTag).build()); |
| Blob toBlob = blobStore.getBlob(toContainer, toName); |
| InputStream is = null; |
| try { |
| is = toBlob.getPayload().openStream(); |
| assertEquals(ByteStreams.toByteArray(is), payload.read()); |
| } finally { |
| Closeables2.closeQuietly(is); |
| } |
| } finally { |
| returnContainer(toContainer); |
| returnContainer(fromContainer); |
| } |
| } |
| |
| @Test(groups = { "integration", "live" }) |
| public void testCopyIfMatchNegative() throws Exception { |
| BlobStore blobStore = view.getBlobStore(); |
| String fromName = "source"; |
| String toName = "to"; |
| ByteSource payload = TestUtils.randomByteSource().slice(0, 1024); |
| Blob blob = blobStore |
| .blobBuilder(fromName) |
| .payload(payload) |
| .contentLength(payload.size()) |
| .build(); |
| String fromContainer = getContainerName(); |
| String toContainer = getContainerName(); |
| try { |
| blobStore.putBlob(fromContainer, blob); |
| try { |
| blobStore.copyBlob(fromContainer, fromName, toContainer, toName, CopyOptions.builder().ifMatch("fake-etag").build()); |
| Fail.failBecauseExceptionWasNotThrown(HttpResponseException.class); |
| } catch (HttpResponseException hre) { |
| assertThat(hre.getResponse().getStatusCode()).isEqualTo(412); |
| } |
| } finally { |
| returnContainer(toContainer); |
| returnContainer(fromContainer); |
| } |
| } |
| |
| @Test(groups = { "integration", "live" }) |
| public void testCopyIfNoneMatch() throws Exception { |
| BlobStore blobStore = view.getBlobStore(); |
| String fromName = "source"; |
| String toName = "to"; |
| ByteSource payload = TestUtils.randomByteSource().slice(0, 1024); |
| Blob blob = blobStore |
| .blobBuilder(fromName) |
| .payload(payload) |
| .contentLength(payload.size()) |
| .build(); |
| String fromContainer = getContainerName(); |
| String toContainer = getContainerName(); |
| try { |
| blobStore.putBlob(fromContainer, blob); |
| blobStore.copyBlob(fromContainer, fromName, toContainer, toName, CopyOptions.builder().ifNoneMatch("fake-etag").build()); |
| Blob toBlob = blobStore.getBlob(toContainer, toName); |
| InputStream is = null; |
| try { |
| is = toBlob.getPayload().openStream(); |
| assertEquals(ByteStreams.toByteArray(is), payload.read()); |
| } finally { |
| Closeables2.closeQuietly(is); |
| } |
| } finally { |
| returnContainer(toContainer); |
| returnContainer(fromContainer); |
| } |
| } |
| |
| @Test(groups = { "integration", "live" }) |
| public void testCopyIfNoneMatchNegative() throws Exception { |
| BlobStore blobStore = view.getBlobStore(); |
| String fromName = "source"; |
| String toName = "to"; |
| ByteSource payload = TestUtils.randomByteSource().slice(0, 1024); |
| Blob blob = blobStore |
| .blobBuilder(fromName) |
| .payload(payload) |
| .contentLength(payload.size()) |
| .build(); |
| String fromContainer = getContainerName(); |
| String toContainer = getContainerName(); |
| try { |
| String eTag = blobStore.putBlob(fromContainer, blob); |
| try { |
| blobStore.copyBlob(fromContainer, fromName, toContainer, toName, CopyOptions.builder().ifNoneMatch(eTag).build()); |
| Fail.failBecauseExceptionWasNotThrown(HttpResponseException.class); |
| } catch (HttpResponseException hre) { |
| assertThat(hre.getResponse().getStatusCode()).isEqualTo(412); |
| } |
| } finally { |
| returnContainer(toContainer); |
| returnContainer(fromContainer); |
| } |
| } |
| |
| @Test(groups = { "integration", "live" }) |
| public void testCopyIfModifiedSince() throws Exception { |
| BlobStore blobStore = view.getBlobStore(); |
| String fromName = "source"; |
| String toName = "to"; |
| ByteSource payload = TestUtils.randomByteSource().slice(0, 1024); |
| Blob blob = blobStore |
| .blobBuilder(fromName) |
| .payload(payload) |
| .contentLength(payload.size()) |
| .build(); |
| String fromContainer = getContainerName(); |
| String toContainer = getContainerName(); |
| try { |
| blobStore.putBlob(fromContainer, blob); |
| Date before = new Date(System.currentTimeMillis() - TimeUnit.HOURS.toMillis(1)); |
| blobStore.copyBlob(fromContainer, fromName, toContainer, toName, CopyOptions.builder().ifModifiedSince(before).build()); |
| Blob toBlob = blobStore.getBlob(toContainer, toName); |
| InputStream is = null; |
| try { |
| is = toBlob.getPayload().openStream(); |
| assertEquals(ByteStreams.toByteArray(is), payload.read()); |
| } finally { |
| Closeables2.closeQuietly(is); |
| } |
| } finally { |
| returnContainer(toContainer); |
| returnContainer(fromContainer); |
| } |
| } |
| |
| @Test(groups = { "integration", "live" }) |
| public void testCopyIfModifiedSinceNegative() throws Exception { |
| BlobStore blobStore = view.getBlobStore(); |
| String fromName = "source"; |
| String toName = "to"; |
| ByteSource payload = TestUtils.randomByteSource().slice(0, 1024); |
| Blob blob = blobStore |
| .blobBuilder(fromName) |
| .payload(payload) |
| .contentLength(payload.size()) |
| .build(); |
| String fromContainer = getContainerName(); |
| String toContainer = getContainerName(); |
| try { |
| blobStore.putBlob(fromContainer, blob); |
| // TODO: some problem with S3 and times in the future? |
| Date after = new Date(System.currentTimeMillis() + TimeUnit.HOURS.toMillis(1)); |
| try { |
| blobStore.copyBlob(fromContainer, fromName, toContainer, toName, CopyOptions.builder().ifModifiedSince(after).build()); |
| Fail.failBecauseExceptionWasNotThrown(HttpResponseException.class); |
| } catch (HttpResponseException hre) { |
| // most object stores return 412 but swift returns 304 |
| assertThat(hre.getResponse().getStatusCode()).isIn(304, 412); |
| } |
| } finally { |
| returnContainer(toContainer); |
| returnContainer(fromContainer); |
| } |
| } |
| |
| @Test(groups = { "integration", "live" }) |
| public void testCopyIfUnmodifiedSince() throws Exception { |
| BlobStore blobStore = view.getBlobStore(); |
| String fromName = "source"; |
| String toName = "to"; |
| ByteSource payload = TestUtils.randomByteSource().slice(0, 1024); |
| Blob blob = blobStore |
| .blobBuilder(fromName) |
| .payload(payload) |
| .contentLength(payload.size()) |
| .build(); |
| String fromContainer = getContainerName(); |
| String toContainer = getContainerName(); |
| try { |
| blobStore.putBlob(fromContainer, blob); |
| Date after = new Date(System.currentTimeMillis() + TimeUnit.HOURS.toMillis(1)); |
| blobStore.copyBlob(fromContainer, fromName, toContainer, toName, CopyOptions.builder().ifUnmodifiedSince(after).build()); |
| Blob toBlob = blobStore.getBlob(toContainer, toName); |
| InputStream is = null; |
| try { |
| is = toBlob.getPayload().openStream(); |
| assertEquals(ByteStreams.toByteArray(is), payload.read()); |
| } finally { |
| Closeables2.closeQuietly(is); |
| } |
| } finally { |
| returnContainer(toContainer); |
| returnContainer(fromContainer); |
| } |
| } |
| |
| @Test(groups = { "integration", "live" }) |
| public void testCopyIfUnmodifiedSinceNegative() throws Exception { |
| BlobStore blobStore = view.getBlobStore(); |
| String fromName = "source"; |
| String toName = "to"; |
| ByteSource payload = TestUtils.randomByteSource().slice(0, 1024); |
| Blob blob = blobStore |
| .blobBuilder(fromName) |
| .payload(payload) |
| .contentLength(payload.size()) |
| .build(); |
| String fromContainer = getContainerName(); |
| String toContainer = getContainerName(); |
| try { |
| blobStore.putBlob(fromContainer, blob); |
| Date before = new Date(System.currentTimeMillis() - TimeUnit.HOURS.toMillis(1)); |
| try { |
| blobStore.copyBlob(fromContainer, fromName, toContainer, toName, CopyOptions.builder().ifUnmodifiedSince(before).build()); |
| Fail.failBecauseExceptionWasNotThrown(HttpResponseException.class); |
| } catch (HttpResponseException hre) { |
| assertThat(hre.getResponse().getStatusCode()).isEqualTo(412); |
| } |
| } finally { |
| returnContainer(toContainer); |
| returnContainer(fromContainer); |
| } |
| } |
| |
| @Test(groups = { "integration", "live" }) |
| public void testMultipartUploadNoPartsAbort() throws Exception { |
| BlobStore blobStore = view.getBlobStore(); |
| String container = getContainerName(); |
| try { |
| String name = "blob-name"; |
| Blob blob = blobStore.blobBuilder(name).build(); |
| MultipartUpload mpu = blobStore.initiateMultipartUpload(container, blob.getMetadata(), new PutOptions()); |
| |
| List<MultipartPart> parts = blobStore.listMultipartUpload(mpu); |
| assertThat(parts).isEqualTo(ImmutableList.of()); |
| |
| blobStore.abortMultipartUpload(mpu); |
| |
| assertThat(blobStore.list(container)).isEmpty(); |
| |
| blob = blobStore.getBlob(container, name); |
| assertThat(blob).isNull(); |
| } finally { |
| returnContainer(container); |
| } |
| } |
| |
| @Test(groups = { "integration", "live" }) |
| public void testMultipartUploadOnePartAbort() throws Exception { |
| BlobStore blobStore = view.getBlobStore(); |
| String container = getContainerName(); |
| try { |
| String name = "blob-name"; |
| Blob blob = blobStore.blobBuilder(name).build(); |
| MultipartUpload mpu = blobStore.initiateMultipartUpload(container, blob.getMetadata(), new PutOptions()); |
| |
| ByteSource byteSource = TestUtils.randomByteSource().slice(0, 1); |
| Payload payload = Payloads.newByteSourcePayload(byteSource); |
| payload.getContentMetadata().setContentLength(byteSource.size()); |
| MultipartPart part = blobStore.uploadMultipartPart(mpu, 1, payload); |
| |
| blobStore.abortMultipartUpload(mpu); |
| |
| assertThat(blobStore.list(container)).isEmpty(); |
| |
| blob = blobStore.getBlob(container, name); |
| assertThat(blob).isNull(); |
| } finally { |
| returnContainer(container); |
| } |
| } |
| |
| @Test(groups = { "integration", "live" }) |
| public void testMultipartUploadSinglePart() throws Exception { |
| BlobStore blobStore = view.getBlobStore(); |
| String container = getContainerName(); |
| try { |
| String name = "blob-name"; |
| PayloadBlobBuilder blobBuilder = blobStore.blobBuilder(name) |
| .userMetadata(ImmutableMap.of("key1", "value1", "key2", "value2")) |
| // TODO: fake payload to add content metadata |
| .payload(new byte[0]); |
| addContentMetadata(blobBuilder); |
| Blob blob = blobBuilder.build(); |
| MultipartUpload mpu = blobStore.initiateMultipartUpload(container, blob.getMetadata(), new PutOptions()); |
| |
| ByteSource byteSource = TestUtils.randomByteSource().slice(0, 1); |
| Payload payload = Payloads.newByteSourcePayload(byteSource); |
| payload.getContentMetadata().setContentLength(byteSource.size()); |
| MultipartPart part = blobStore.uploadMultipartPart(mpu, 1, payload); |
| |
| List<MultipartPart> parts = blobStore.listMultipartUpload(mpu); |
| assertThat(parts).hasSize(1); |
| assertThat(parts.get(0).partNumber()).isEqualTo(part.partNumber()); |
| assertThat(parts.get(0).partSize()).isEqualTo(part.partSize()); |
| assertThat(parts.get(0).partETag()).isEqualTo(part.partETag()); |
| // not checking lastModified since B2, S3, and Swift do not return it from uploadMultipartPart |
| |
| blobStore.completeMultipartUpload(mpu, ImmutableList.of(part)); |
| |
| Blob newBlob = blobStore.getBlob(container, name); |
| assertThat(newBlob).isNotNull(); |
| assertThat(ByteStreams2.toByteArrayAndClose(newBlob.getPayload().openStream())).isEqualTo(byteSource.read()); |
| checkContentMetadata(newBlob); |
| checkUserMetadata(newBlob.getMetadata().getUserMetadata(), blob.getMetadata().getUserMetadata()); |
| checkMPUParts(newBlob, parts); |
| } finally { |
| returnContainer(container); |
| } |
| } |
| |
| @Test(groups = { "integration", "live" }) |
| public void testMultipartUploadMultipleParts() throws Exception { |
| BlobStore blobStore = view.getBlobStore(); |
| String container = getContainerName(); |
| try { |
| String name = "blob-name"; |
| PayloadBlobBuilder blobBuilder = blobStore.blobBuilder(name) |
| .userMetadata(ImmutableMap.of("key1", "value1", "key2", "value2")) |
| // TODO: fake payload to add content metadata |
| .payload(new byte[0]); |
| addContentMetadata(blobBuilder); |
| Blob blob = blobBuilder.build(); |
| MultipartUpload mpu = blobStore.initiateMultipartUpload(container, blob.getMetadata(), new PutOptions()); |
| |
| ByteSource byteSource = TestUtils.randomByteSource().slice(0, blobStore.getMinimumMultipartPartSize() + 1); |
| ByteSource byteSource1 = byteSource.slice(0, blobStore.getMinimumMultipartPartSize()); |
| ByteSource byteSource2 = byteSource.slice(blobStore.getMinimumMultipartPartSize(), 1); |
| Payload payload1 = Payloads.newByteSourcePayload(byteSource1); |
| Payload payload2 = Payloads.newByteSourcePayload(byteSource2); |
| payload1.getContentMetadata().setContentLength(byteSource1.size()); |
| payload2.getContentMetadata().setContentLength(byteSource2.size()); |
| MultipartPart part1 = blobStore.uploadMultipartPart(mpu, 1, payload1); |
| MultipartPart part2 = blobStore.uploadMultipartPart(mpu, 2, payload2); |
| |
| List<MultipartPart> parts = blobStore.listMultipartUpload(mpu); |
| assertThat(parts).hasSize(2); |
| assertThat(parts.get(0).partNumber()).isEqualTo(part1.partNumber()); |
| assertThat(parts.get(0).partSize()).isEqualTo(part1.partSize()); |
| assertThat(parts.get(0).partETag()).isEqualTo(part1.partETag()); |
| assertThat(parts.get(1).partNumber()).isEqualTo(part2.partNumber()); |
| assertThat(parts.get(1).partSize()).isEqualTo(part2.partSize()); |
| assertThat(parts.get(1).partETag()).isEqualTo(part2.partETag()); |
| // not checking lastModified since B2, S3, and Swift do not return it from uploadMultipartPart |
| |
| blobStore.completeMultipartUpload(mpu, ImmutableList.of(part1, part2)); |
| |
| Blob newBlob = blobStore.getBlob(container, name); |
| assertThat(ByteStreams2.toByteArrayAndClose(newBlob.getPayload().openStream())).isEqualTo(byteSource.read()); |
| checkContentMetadata(newBlob); |
| checkUserMetadata(newBlob.getMetadata().getUserMetadata(), blob.getMetadata().getUserMetadata()); |
| checkMPUParts(newBlob, parts); |
| } finally { |
| returnContainer(container); |
| } |
| } |
| |
| @Test(groups = { "integration", "live" }) |
| public void testListMultipartUploads() throws Exception { |
| BlobStore blobStore = view.getBlobStore(); |
| String name = "blob-name"; |
| PayloadBlobBuilder blobBuilder = blobStore.blobBuilder(name) |
| .userMetadata(ImmutableMap.of("key1", "value1", "key2", "value2")) |
| // TODO: fake payload to add content metadata |
| .payload(new byte[0]); |
| addContentMetadata(blobBuilder); |
| Blob blob = blobBuilder.build(); |
| MultipartUpload mpu = null; |
| |
| String container = getContainerName(); |
| try { |
| List<MultipartUpload> uploads = blobStore.listMultipartUploads(container); |
| assertThat(uploads).isEmpty(); |
| |
| mpu = blobStore.initiateMultipartUpload(container, blob.getMetadata(), new PutOptions()); |
| |
| // some providers like Azure cannot list an MPU until the first blob is uploaded |
| assertThat(uploads.size()).isBetween(0, 1); |
| |
| // B2 requires at least two parts to call complete |
| ByteSource byteSource = TestUtils.randomByteSource().slice(0, blobStore.getMinimumMultipartPartSize() + 1); |
| ByteSource byteSource1 = byteSource.slice(0, blobStore.getMinimumMultipartPartSize()); |
| ByteSource byteSource2 = byteSource.slice(blobStore.getMinimumMultipartPartSize(), 1); |
| Payload payload1 = Payloads.newByteSourcePayload(byteSource1); |
| Payload payload2 = Payloads.newByteSourcePayload(byteSource2); |
| payload1.getContentMetadata().setContentLength(byteSource1.size()); |
| payload2.getContentMetadata().setContentLength(byteSource2.size()); |
| MultipartPart part1 = blobStore.uploadMultipartPart(mpu, 1, payload1); |
| MultipartPart part2 = blobStore.uploadMultipartPart(mpu, 2, payload2); |
| |
| uploads = blobStore.listMultipartUploads(container); |
| assertThat(uploads).hasSize(1); |
| |
| blobStore.completeMultipartUpload(mpu, ImmutableList.of(part1, part2)); |
| mpu = null; |
| |
| uploads = blobStore.listMultipartUploads(container); |
| assertThat(uploads).isEmpty(); |
| |
| // cannot test abort since Azure does not have explicit support |
| } finally { |
| if (mpu != null) { |
| blobStore.abortMultipartUpload(mpu); |
| } |
| returnContainer(container); |
| } |
| } |
| |
| @Test(groups = { "integration", "live" }, expectedExceptions = {KeyNotFoundException.class}) |
| public void testCopy404BlobFail() throws Exception { |
| BlobStore blobStore = view.getBlobStore(); |
| String container = getContainerName(); |
| try { |
| blobStore.copyBlob(container, "blob", container, "blob2", CopyOptions.NONE); |
| } finally { |
| returnContainer(container); |
| } |
| } |
| |
| @Test(groups = { "integration", "live" }, expectedExceptions = {KeyNotFoundException.class}) |
| public void testCopy404BlobMetaFail() throws Exception { |
| BlobStore blobStore = view.getBlobStore(); |
| String container = getContainerName(); |
| try { |
| blobStore.copyBlob(container, "blob", container, "blob2", |
| CopyOptions.builder().userMetadata(ImmutableMap.of("x", "1")).build()); |
| } finally { |
| returnContainer(container); |
| } |
| } |
| |
| protected void validateMetadata(BlobMetadata metadata) throws IOException { |
| assert metadata.getContentMetadata().getContentType().startsWith("text/plain") : metadata.getContentMetadata() |
| .getContentType(); |
| assertEquals(metadata.getContentMetadata().getContentLength(), Long.valueOf(TEST_STRING.length())); |
| assertEquals(metadata.getUserMetadata().get("adrian"), "powderpuff"); |
| checkMD5(metadata); |
| } |
| |
| protected void checkMD5(BlobMetadata metadata) throws IOException { |
| assertEquals(metadata.getContentMetadata().getContentMD5(), md5().hashString(TEST_STRING, UTF_8).asBytes()); |
| } |
| |
| /** @return ByteSource containing a random length 0..length of random bytes. */ |
| private static ByteSource createTestInput(int length) { |
| return TestUtils.randomByteSource().slice(0, new Random().nextInt(length)); |
| } |
| } |