/** * Copyright Microsoft Corporation * * Licensed 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 com.microsoft.azure.storage.blob; import com.microsoft.azure.storage.AccessCondition; import com.microsoft.azure.storage.Constants; import com.microsoft.azure.storage.NameValidator; import com.microsoft.azure.storage.OperationContext; import com.microsoft.azure.storage.RetryNoRetry; import com.microsoft.azure.storage.SendingRequestEvent; import com.microsoft.azure.storage.StorageCredentialsAnonymous; import com.microsoft.azure.storage.StorageCredentialsSharedAccessSignature; import com.microsoft.azure.storage.StorageEvent; import com.microsoft.azure.storage.StorageException; import com.microsoft.azure.storage.TestRunners; import com.microsoft.azure.storage.core.Utility; import com.microsoft.azure.storage.file.CloudFile; import com.microsoft.azure.storage.file.CloudFileShare; import com.microsoft.azure.storage.file.FileProperties; import com.microsoft.azure.storage.file.FileTestHelper; import com.microsoft.azure.storage.file.SharedAccessFilePermissions; import com.microsoft.azure.storage.file.SharedAccessFilePolicy; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.experimental.categories.Category; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.net.HttpURLConnection; import java.net.URI; import java.net.URISyntaxException; import java.security.InvalidKeyException; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.EnumSet; import java.util.GregorianCalendar; import java.util.List; import java.util.Map; import java.util.Random; import java.util.TimeZone; import com.microsoft.azure.storage.StorageErrorCodeStrings; import com.microsoft.azure.storage.TestRunners.CloudTests; import com.microsoft.azure.storage.TestRunners.DevFabricTests; import com.microsoft.azure.storage.TestRunners.DevStoreTests; import com.microsoft.azure.storage.TestRunners.SlowTests; import static org.junit.Assert.*; /** * Block Blob Tests */ @Category(CloudTests.class) public class CloudBlockBlobTests { protected CloudBlobContainer container; @Before public void blobEncryptionTestMethodSetUp() throws URISyntaxException, StorageException { this.container = BlobTestHelper.getRandomContainerReference(); this.container.create(); } @After public void blobEncryptionTestMethodTearDown() throws StorageException { this.container.deleteIfExists(); } /** * Create a block blob. * * @throws StorageException * @throws URISyntaxException * @throws IOException */ @Test @Category({ DevFabricTests.class, DevStoreTests.class }) public void testBlockBlobCreate() throws StorageException, URISyntaxException, IOException { final CloudBlockBlob blob = this.container.getBlockBlobReference(BlobTestHelper .generateRandomBlobNameWithPrefix("testBlob")); assertFalse(blob.exists()); // Create blob.uploadText("text"); assertTrue(blob.exists()); // Create again (should succeed) blob.uploadText("text"); assertTrue(blob.exists()); // Create again, specifying not to if it already exists // This should fail // Add 15 min to account for clock skew Calendar cal = Calendar.getInstance(); cal.setTime(new Date()); cal.add(Calendar.MINUTE, 15); AccessCondition condition = new AccessCondition(); condition.setIfModifiedSinceDate(cal.getTime()); try { blob.uploadText("text", null, condition, null, null); fail("Create should fail do to access condition."); } catch (StorageException ex) { assertEquals(HttpURLConnection.HTTP_PRECON_FAILED, ex.getHttpStatusCode()); assertEquals("The condition specified using HTTP conditional header(s) is not met.", ex.getMessage()); assertEquals("ConditionNotMet", ex.getErrorCode()); } } /** * Delete a block blob. * * @throws StorageException * @throws URISyntaxException * @throws IOException */ @Test @Category({ DevFabricTests.class, DevStoreTests.class }) public void testBlockBlobDelete() throws StorageException, URISyntaxException, IOException { final CloudBlockBlob blob = this.container.getBlockBlobReference(BlobTestHelper .generateRandomBlobNameWithPrefix("testBlob")); assertFalse(blob.exists()); // create blob.uploadText("text"); assertTrue(blob.exists()); // delete blob.delete(); assertFalse(blob.exists()); // delete again, should fail as it doesn't exist try { blob.delete(); fail("Delete should fail as blob does not exist."); } catch (StorageException ex) { assertEquals(HttpURLConnection.HTTP_NOT_FOUND, ex.getHttpStatusCode()); assertEquals("The specified blob does not exist.", ex.getMessage()); assertEquals("BlobNotFound", ex.getErrorCode()); } } /** * Test blob name validation. */ @Test public void testCloudBlobNameValidation() { NameValidator.validateBlobName("alpha"); NameValidator.validateBlobName("4lphanum3r1c"); NameValidator.validateBlobName("CAPSLOCK"); NameValidator.validateBlobName("white space"); NameValidator.validateBlobName("0th3r(h@racter$"); NameValidator.validateBlobName(new String(new char[253]).replace("\0", "a/a")); invalidBlobTestHelper("", "No empty strings.", "Invalid blob name. The name may not be null, empty, or whitespace only."); invalidBlobTestHelper(null, "No null strings.", "Invalid blob name. The name may not be null, empty, or whitespace only."); invalidBlobTestHelper(new String(new char[1025]).replace("\0", "n"), "Maximum 1024 characters.", "Invalid blob name length. The name must be between 1 and 1024 characters long."); invalidBlobTestHelper(new String(new char[254]).replace("\0", "a/a"), "Maximum 254 path segments.", "The count of URL path segments (strings between '/' characters) as part of the blob name cannot exceed 254."); } private void invalidBlobTestHelper(String blobName, String failMessage, String exceptionMessage) { try { NameValidator.validateBlobName(blobName); fail(failMessage); } catch (IllegalArgumentException e) { assertEquals(exceptionMessage, e.getMessage()); } } @Test public void testBlobUriOnlyConstructors() throws URISyntaxException, StorageException, InvalidKeyException { URI blobURI = new URI(this.container.getUri().toString() + "/anonblob"); CloudBlockBlob blob = new CloudBlockBlob(blobURI); assertEquals("anonblob", blob.getName()); assertNotNull(blob.getServiceClient()); assertEquals(StorageCredentialsAnonymous.class, blob.getServiceClient().getCredentials().getClass()); blob = this.container.getBlockBlobReference("anonblob"); String sas = blob.generateSharedAccessSignature(null, "dummyPolicy"); blobURI = new URI(this.container.getUri().toString() + "/anonblob?" + sas); blob = new CloudBlockBlob(blobURI); assertEquals("anonblob", blob.getName()); assertNotNull(blob.getServiceClient()); assertEquals(StorageCredentialsSharedAccessSignature.class, blob.getServiceClient().getCredentials().getClass()); } @Test @Category(SlowTests.class) public void testCopyBlockBlobSasToSasTest() throws InvalidKeyException, URISyntaxException, StorageException, IOException, InterruptedException { this.doCloudBlockBlobCopy(true, true); } @Test @Category(SlowTests.class) public void testCopyBlockBlobToSasTest() throws InvalidKeyException, URISyntaxException, StorageException, IOException, InterruptedException { this.doCloudBlockBlobCopy(false, true); } @Test @Category(SlowTests.class) public void testCopyBlockBlobSasTest() throws InvalidKeyException, URISyntaxException, StorageException, IOException, InterruptedException { this.doCloudBlockBlobCopy(true, false); } @Test @Category({ DevFabricTests.class, DevStoreTests.class, SlowTests.class }) public void testCopyBlockBlobTest() throws InvalidKeyException, URISyntaxException, StorageException, IOException, InterruptedException { this.doCloudBlockBlobCopy(false, false); } @Test public void testCopyWithChineseChars() throws StorageException, IOException, URISyntaxException { String data = "sample data chinese chars 阿䶵"; CloudBlockBlob copySource = container.getBlockBlobReference("sourcechinescharsblob阿䶵.txt"); copySource.uploadText(data); assertEquals(this.container.getUri() + "/sourcechinescharsblob阿䶵.txt", copySource.getUri().toString()); assertEquals(this.container.getUri() + "/sourcechinescharsblob%E9%98%BF%E4%B6%B5.txt", copySource.getUri().toASCIIString()); CloudBlockBlob copyDestination = container.getBlockBlobReference("destchinesecharsblob阿䶵.txt"); assertEquals(this.container.getUri() + "/destchinesecharsblob阿䶵.txt", copyDestination.getUri().toString()); assertEquals(this.container.getUri() + "/destchinesecharsblob%E9%98%BF%E4%B6%B5.txt", copyDestination.getUri().toASCIIString()); OperationContext ctx = new OperationContext(); ctx.getSendingRequestEventHandler().addListener(new StorageEvent<SendingRequestEvent>() { @Override public void eventOccurred(SendingRequestEvent eventArg) { HttpURLConnection con = (HttpURLConnection) eventArg.getConnectionObject(); // Test the copy destination request url assertEquals(CloudBlockBlobTests.this.container.getUri() + "/destchinesecharsblob%E9%98%BF%E4%B6%B5.txt", con.getURL().toString()); // Test the copy source request property assertEquals(CloudBlockBlobTests.this.container.getUri() + "/sourcechinescharsblob%E9%98%BF%E4%B6%B5.txt", con.getRequestProperty("x-ms-copy-source")); } }); copyDestination.startCopy(copySource.getUri(), null, null, null, ctx); copyDestination.startCopy(copySource, null, null, null, ctx); } @Test @Category({ DevFabricTests.class, DevStoreTests.class, SlowTests.class }) public void testCopyBlockBlobWithMetadataOverride() throws URISyntaxException, StorageException, IOException, InterruptedException { Calendar calendar = Calendar.getInstance(Utility.UTC_ZONE); String data = "String data"; CloudBlockBlob source = this.container.getBlockBlobReference("source"); BlobTestHelper.setBlobProperties(source); source.uploadText(data, Constants.UTF8_CHARSET, null, null, null); source.getMetadata().put("Test", "value"); source.uploadMetadata(); CloudBlockBlob copy = this.container.getBlockBlobReference("copy"); copy.getMetadata().put("Test2", "value2"); String copyId = copy.startCopy(BlobTestHelper.defiddler(source)); BlobTestHelper.waitForCopy(copy); assertEquals(CopyStatus.SUCCESS, copy.getCopyState().getStatus()); assertEquals(source.getSnapshotQualifiedUri().getPath(), copy.getCopyState().getSource().getPath()); assertEquals(data.length(), copy.getCopyState().getTotalBytes().intValue()); assertEquals(data.length(), copy.getCopyState().getBytesCopied().intValue()); assertEquals(copyId, copy.getCopyState().getCopyId()); assertTrue(copy.getCopyState().getCompletionTime().compareTo(new Date(calendar.get(Calendar.MINUTE) - 1)) > 0); String copyData = copy.downloadText(Constants.UTF8_CHARSET, null, null, null); assertEquals(data, copyData); copy.downloadAttributes(); source.downloadAttributes(); BlobProperties prop1 = copy.getProperties(); BlobProperties prop2 = source.getProperties(); assertEquals(prop1.getCacheControl(), prop2.getCacheControl()); assertEquals(prop1.getContentEncoding(), prop2.getContentEncoding()); assertEquals(prop1.getContentDisposition(), prop2.getContentDisposition()); assertEquals(prop1.getContentLanguage(), prop2.getContentLanguage()); assertEquals(prop1.getContentMD5(), prop2.getContentMD5()); assertEquals(prop1.getContentType(), prop2.getContentType()); assertEquals("value2", copy.getMetadata().get("Test2")); assertFalse(copy.getMetadata().containsKey("Test")); copy.delete(); } @Test @Category({ DevFabricTests.class, DevStoreTests.class, SlowTests.class }) public void testCopyBlockBlobFromSnapshot() throws StorageException, IOException, URISyntaxException, InterruptedException { CloudBlockBlob source = this.container.getBlockBlobReference("source"); String data = "String data"; BlobTestHelper.setBlobProperties(source); source.uploadText(data, Constants.UTF8_CHARSET, null, null, null); source.getMetadata().put("Test", "value"); source.uploadMetadata(); CloudBlockBlob snapshot = (CloudBlockBlob) source.createSnapshot(); //Modify source String newData = "Hello"; source.getMetadata().put("Test", "newvalue"); source.uploadMetadata(); source.getProperties().setContentMD5(null); source.uploadText(newData, Constants.UTF8_CHARSET, null, null, null); assertEquals(newData, source.downloadText(Constants.UTF8_CHARSET, null, null, null)); assertEquals(data, snapshot.downloadText(Constants.UTF8_CHARSET, null, null, null)); source.downloadAttributes(); snapshot.downloadAttributes(); assertFalse(source.getMetadata().get("Test").equals(snapshot.getMetadata().get("Test"))); CloudBlockBlob copy = this.container.getBlockBlobReference("copy"); String copyId = copy.startCopy(BlobTestHelper.defiddler(snapshot)); BlobTestHelper.waitForCopy(copy); copy.downloadAttributes(); assertEquals(CopyStatus.SUCCESS, copy.getCopyState().getStatus()); assertEquals(data, copy.downloadText(Constants.UTF8_CHARSET, null, null, null)); assertEquals(copyId, copy.getProperties().getCopyState().getCopyId()); copy.downloadAttributes(); BlobProperties prop1 = copy.getProperties(); BlobProperties prop2 = snapshot.getProperties(); assertEquals(prop1.getCacheControl(), prop2.getCacheControl()); assertEquals(prop1.getContentEncoding(), prop2.getContentEncoding()); assertEquals(prop1.getContentDisposition(), prop2.getContentDisposition()); assertEquals(prop1.getContentLanguage(), prop2.getContentLanguage()); assertEquals(prop1.getContentMD5(), prop2.getContentMD5()); assertEquals(prop1.getContentType(), prop2.getContentType()); assertEquals("value", copy.getMetadata().get("Test")); copy.delete(); } /** * Start copying a blob and then abort * * @throws StorageException * @throws URISyntaxException * @throws IOException * @throws InterruptedException */ @Test @Category({ DevFabricTests.class, DevStoreTests.class }) public void testCopyFromBlobAbortTest() throws StorageException, URISyntaxException, IOException { final int length = 128; CloudBlockBlob originalBlob = (CloudBlockBlob) BlobTestHelper.uploadNewBlob( this.container, BlobType.BLOCK_BLOB, "originalBlob", length, null); CloudBlockBlob copyBlob = this.container.getBlockBlobReference(originalBlob.getName() + "copyed"); copyBlob.startCopy(originalBlob); try { copyBlob.abortCopy(copyBlob.getProperties().getCopyState().getCopyId()); } catch (StorageException e) { if (!e.getErrorCode().contains("NoPendingCopyOperation")) { throw e; } } } @Test @Category(SlowTests.class) public void testCopyFileSas() throws InvalidKeyException, URISyntaxException, StorageException, IOException, InterruptedException { // Create source on server. final CloudFileShare share = FileTestHelper.getRandomShareReference(); try { share.create(); final CloudFile source = share.getRootDirectoryReference().getFileReference("source"); final String data = "String data"; source.getMetadata().put("Test", "value"); source.uploadText(data, Constants.UTF8_CHARSET, null, null, null); Calendar cal = Calendar.getInstance(Utility.UTC_ZONE); cal.add(Calendar.MINUTE, 5); // Source SAS must have read permissions SharedAccessFilePolicy policy = new SharedAccessFilePolicy(); policy.setPermissions(EnumSet.of(SharedAccessFilePermissions.READ)); policy.setSharedAccessExpiryTime(cal.getTime()); String sasToken = source.generateSharedAccessSignature(policy, null, null); // Get destination reference final CloudBlockBlob destination = this.container.getBlockBlobReference("destination"); // Start copy and wait for completion StorageCredentialsSharedAccessSignature credentials = new StorageCredentialsSharedAccessSignature(sasToken); String copyId = destination.startCopy(new CloudFile(credentials.transformUri(source.getUri()))); BlobTestHelper.waitForCopy(destination); destination.downloadAttributes(); assertNotNull(destination.getProperties().getEtag()); // Check original file references for equality assertEquals(CopyStatus.SUCCESS, destination.getCopyState().getStatus()); assertEquals(source.getServiceClient().getCredentials().transformUri(source.getUri()).getPath(), destination.getCopyState().getSource().getPath()); assertEquals(data.length(), destination.getCopyState().getTotalBytes().intValue()); assertEquals(data.length(), destination.getCopyState().getBytesCopied().intValue()); assertEquals(copyId, destination.getProperties().getCopyState().getCopyId()); // Attempt to abort the completed copy operation. try { destination.abortCopy(destination.getCopyState().getCopyId()); fail(); } catch (StorageException ex) { assertEquals(HttpURLConnection.HTTP_CONFLICT, ex.getHttpStatusCode()); } String copyData = destination.downloadText(Constants.UTF8_CHARSET, null, null, null); assertEquals(data, copyData); source.downloadAttributes(); BlobProperties prop1 = destination.getProperties(); FileProperties prop2 = source.getProperties(); assertEquals(prop1.getCacheControl(), prop2.getCacheControl()); assertEquals(prop1.getContentEncoding(), prop2.getContentEncoding()); assertEquals(prop1.getContentLanguage(), prop2.getContentLanguage()); assertEquals(prop1.getContentMD5(), prop2.getContentMD5()); assertEquals(prop1.getContentType(), prop2.getContentType()); assertEquals("value", destination.getMetadata().get("Test")); assertEquals(1, destination.getMetadata().size()); } finally { share.deleteIfExists(); } } @Test @Category({ DevFabricTests.class, DevStoreTests.class, SlowTests.class }) public void testCopyFileWithMetadataOverride() throws URISyntaxException, StorageException, IOException, InterruptedException, InvalidKeyException { Calendar calendar = Calendar.getInstance(Utility.UTC_ZONE); String data = "String data"; final CloudFileShare share = FileTestHelper.getRandomShareReference(); try { share.create(); final CloudFile source = share.getRootDirectoryReference().getFileReference("source"); FileTestHelper.setFileProperties(source); // do this to make sure the set MD5 can be compared, otherwise when the dummy value // doesn't match the actual MD5 an exception would be thrown BlobRequestOptions options = new BlobRequestOptions(); options.setDisableContentMD5Validation(true); source.getMetadata().put("Test", "value"); source.uploadText(data); calendar.add(Calendar.MINUTE, 5); // Source SAS must have read permissions SharedAccessFilePolicy policy = new SharedAccessFilePolicy(); policy.setPermissions(EnumSet.of(SharedAccessFilePermissions.READ)); policy.setSharedAccessExpiryTime(calendar.getTime()); String sasToken = source.generateSharedAccessSignature(policy, null, null); // Get source BlockBlob reference StorageCredentialsSharedAccessSignature credentials = new StorageCredentialsSharedAccessSignature(sasToken); CloudBlockBlob destination = this.container.getBlockBlobReference("copy"); destination.getMetadata().put("Test2", "value2"); String copyId = destination.startCopy( FileTestHelper.defiddler(new CloudFile(credentials.transformUri(source.getUri())))); BlobTestHelper.waitForCopy(destination); destination.downloadAttributes(); assertEquals(CopyStatus.SUCCESS, destination.getCopyState().getStatus()); assertEquals(source.getServiceClient().getCredentials().transformUri(source.getUri()).getPath(), destination.getCopyState().getSource().getPath()); assertEquals(data.length(), destination.getCopyState().getTotalBytes().intValue()); assertEquals(data.length(), destination.getCopyState().getBytesCopied().intValue()); assertEquals(copyId, destination.getCopyState().getCopyId()); assertTrue(0 < destination.getCopyState().getCompletionTime().compareTo( new Date(calendar.get(Calendar.MINUTE) - 6))); String copyData = destination.downloadText(Constants.UTF8_CHARSET, null, options, null); assertEquals(data, copyData); source.downloadAttributes(); BlobProperties prop1 = destination.getProperties(); FileProperties prop2 = source.getProperties(); assertEquals(prop1.getCacheControl(), prop2.getCacheControl()); assertEquals(prop1.getContentEncoding(), prop2.getContentEncoding()); assertEquals(prop1.getContentDisposition(), prop2.getContentDisposition()); assertEquals(prop1.getContentLanguage(), prop2.getContentLanguage()); assertEquals(prop1.getContentMD5(), prop2.getContentMD5()); assertEquals(prop1.getContentType(), prop2.getContentType()); assertEquals("value2", destination.getMetadata().get("Test2")); assertFalse(destination.getMetadata().containsKey("Test")); assertEquals(1, destination.getMetadata().size()); } finally { share.deleteIfExists(); } } /** * Start copying a file and then abort * * @throws StorageException * @throws URISyntaxException * @throws IOException * @throws InvalidKeyException * @throws InterruptedException */ @Test @Category({ DevFabricTests.class, DevStoreTests.class }) public void testCopyFileAbort() throws StorageException, URISyntaxException, IOException, InvalidKeyException, InterruptedException { final int length = 128; final CloudFileShare share = FileTestHelper.getRandomShareReference(); share.create(); final CloudFile source = FileTestHelper.uploadNewFile(share, length, null); // Source SAS must have read permissions SharedAccessFilePolicy policy = new SharedAccessFilePolicy(); policy.setPermissions(EnumSet.of(SharedAccessFilePermissions.READ)); Calendar cal = Calendar.getInstance(Utility.UTC_ZONE); cal.add(Calendar.MINUTE, 5); policy.setSharedAccessExpiryTime(cal.getTime()); String sasToken = source.generateSharedAccessSignature(policy, null, null); // Start copy and wait for completion final CloudBlockBlob destination = this.container.getBlockBlobReference(source.getName() + "copyed"); StorageCredentialsSharedAccessSignature credentials = new StorageCredentialsSharedAccessSignature(sasToken); destination.startCopy(new CloudFile(credentials.transformUri(source.getUri()))); try { destination.abortCopy(destination.getProperties().getCopyState().getCopyId()); BlobTestHelper.waitForCopy(destination); fail(); } catch (StorageException e) { if (!e.getErrorCode().contains("NoPendingCopyOperation")) { throw e; } } finally { share.deleteIfExists(); } } @Test @Category({ DevFabricTests.class, DevStoreTests.class }) public void testDeleteBlobIfExists() throws URISyntaxException, StorageException, IOException { final CloudBlockBlob blob1 = this.container.getBlockBlobReference(BlobTestHelper .generateRandomBlobNameWithPrefix("testBlob")); assertFalse(blob1.exists()); assertFalse(blob1.deleteIfExists()); blob1.uploadText("test1"); assertTrue(blob1.exists()); assertTrue(blob1.deleteIfExists()); assertFalse(blob1.deleteIfExists()); // check if second condition works in delete if exists OperationContext ctx = new OperationContext(); ctx.getSendingRequestEventHandler().addListener(new StorageEvent<SendingRequestEvent>() { @Override public void eventOccurred(SendingRequestEvent eventArg) { if (((HttpURLConnection) eventArg.getConnectionObject()).getRequestMethod().equals("DELETE")) { try { blob1.delete(); assertFalse(blob1.exists()); } catch (StorageException e) { fail("Delete should succeed."); } } } }); // The second delete of a blob will return a 404 blob1.uploadText("test1"); assertFalse(blob1.deleteIfExists(DeleteSnapshotsOption.NONE, null, null, ctx)); } /** * Create a snapshot * * @throws StorageException * @throws URISyntaxException * @throws IOException * @throws InterruptedException */ @Test @Category({ DevFabricTests.class, DevStoreTests.class }) public void testBlobSnapshotValidationTest() throws StorageException, URISyntaxException, IOException { final int length = 1024; CloudBlockBlob blockBlobRef = (CloudBlockBlob) BlobTestHelper.uploadNewBlob(this.container, BlobType.BLOCK_BLOB, "originalBlob", length, null); final CloudBlob blobSnapshot = blockBlobRef.createSnapshot(); for (ListBlobItem blob : this.container.listBlobs(null, true, EnumSet.allOf(BlobListingDetails.class), null, null)) { final ByteArrayOutputStream outStream = new ByteArrayOutputStream(length); ((CloudBlob) blob).download(outStream); } ByteArrayOutputStream outStream = new ByteArrayOutputStream(length); blobSnapshot.download(outStream); byte[] retrievedBuff = outStream.toByteArray(); assertEquals(length, retrievedBuff.length); // Read operation should work fine. blobSnapshot.downloadAttributes(); final CloudBlockBlob blobSnapshotUsingRootUri = this.container.getBlockBlobReference(blockBlobRef.getName(), blobSnapshot.getSnapshotID()); outStream = new ByteArrayOutputStream(length); blobSnapshotUsingRootUri.download(outStream); retrievedBuff = outStream.toByteArray(); assertEquals(length, retrievedBuff.length); assertEquals(blobSnapshot.getSnapshotID(), blobSnapshotUsingRootUri.getSnapshotID()); // Expect an IllegalArgumentException from upload. try { final Random randGenerator = new Random(); final byte[] buff = new byte[length]; randGenerator.nextBytes(buff); blobSnapshot.upload(new ByteArrayInputStream(buff), -1); fail("Expect an IllegalArgumentException from upload"); } catch (IllegalArgumentException e) { assertEquals("Cannot perform this operation on a blob representing a snapshot.", e.getMessage()); } // Expect an IllegalArgumentException from uploadMetadata. try { blobSnapshot.uploadMetadata(); fail("Expect an IllegalArgumentException from uploadMetadata"); } catch (IllegalArgumentException e) { assertEquals("Cannot perform this operation on a blob representing a snapshot.", e.getMessage()); } // Expect an IllegalArgumentException from uploadProperties. try { blobSnapshot.uploadProperties(); fail("Expect an IllegalArgumentException from uploadProperties"); } catch (IllegalArgumentException e) { assertEquals("Cannot perform this operation on a blob representing a snapshot.", e.getMessage()); } // Expect an IllegalArgumentException from createSnapshot. try { blobSnapshot.createSnapshot(); fail("Expect an IllegalArgumentException from createSnapshot"); } catch (IllegalArgumentException e) { assertEquals("Cannot perform this operation on a blob representing a snapshot.", e.getMessage()); } } /** * Create a blob and try to download a range of its contents * * @throws StorageException * @throws URISyntaxException * @throws IOException * @throws InterruptedException */ @Test @Category({ DevFabricTests.class, DevStoreTests.class }) public void testBlobDownloadRangeValidationTest() throws StorageException, URISyntaxException, IOException { final int blockLength = Constants.MB; final int length = 5 * blockLength; final String blockBlobName = BlobTestHelper.generateRandomBlobNameWithPrefix("testBlockBlob"); final CloudBlockBlob blockBlobRef = this.container.getBlockBlobReference(blockBlobName); ArrayList<BlockEntry> blockList = new ArrayList<BlockEntry>(); for (int i = 1; i <= 5; i++) { String blockID = String.format("%08d", i); blockBlobRef .uploadBlock(blockID, BlobTestHelper.getRandomDataStream(length), blockLength, null, null, null); blockList.add(new BlockEntry(blockID, BlockSearchMode.LATEST)); } blockBlobRef.commitBlockList(blockList); //Download full blob blockBlobRef.download(new ByteArrayOutputStream()); assertEquals(length, blockBlobRef.getProperties().getLength()); //Download blob range. ByteArrayOutputStream downloadStream = new ByteArrayOutputStream(); blockBlobRef.downloadRange(0, (long) 100, downloadStream); assertEquals(length, blockBlobRef.getProperties().getLength()); //Download block list. blockBlobRef.downloadBlockList(); assertEquals(length, blockBlobRef.getProperties().getLength()); } @Test @Category({ DevFabricTests.class, DevStoreTests.class }) public void testCommitBlockListContentMd5() throws URISyntaxException, StorageException, IOException { int length = 1024; byte[] buffer = BlobTestHelper.getRandomBuffer(length); Map<String, BlockEntry> blocks = BlobTestHelper.getBlockEntryList(3); String blobName = BlobTestHelper.generateRandomBlobNameWithPrefix("blob1"); CloudBlockBlob blob = this.container.getBlockBlobReference(blobName); for (BlockEntry block : blocks.values()) { blob.uploadBlock(block.getId(), new ByteArrayInputStream(buffer), length); } OperationContext ctx = new OperationContext(); ctx.getSendingRequestEventHandler().addListener(new StorageEvent<SendingRequestEvent>() { @Override public void eventOccurred(SendingRequestEvent eventArg) { HttpURLConnection conn = (HttpURLConnection)eventArg.getConnectionObject(); assertNull(conn.getRequestProperty("Content-MD5")); } }); blob.commitBlockList(blocks.values(), null, null, ctx); BlobRequestOptions opt = new BlobRequestOptions(); opt.setUseTransactionalContentMD5(true); ctx = new OperationContext(); ctx.getSendingRequestEventHandler().addListener(new StorageEvent<SendingRequestEvent>() { @Override public void eventOccurred(SendingRequestEvent eventArg) { HttpURLConnection conn = (HttpURLConnection)eventArg.getConnectionObject(); assertNotNull(conn.getRequestProperty("Content-MD5")); } }); blob.commitBlockList(blocks.values(), null, opt, ctx); } @Test @Category({ DevFabricTests.class, DevStoreTests.class }) public void testDownloadBlockList() throws URISyntaxException, StorageException, IOException { int length = 1024; byte[] buffer = BlobTestHelper.getRandomBuffer(length); Map<String, BlockEntry> blocks = BlobTestHelper.getBlockEntryList(3); Map<String, BlockEntry> extraBlocks = BlobTestHelper.getBlockEntryList(2); String blobName = BlobTestHelper.generateRandomBlobNameWithPrefix("blob1"); CloudBlockBlob blob = this.container.getBlockBlobReference(blobName); for (BlockEntry block : blocks.values()) { blob.uploadBlock(block.getId(), new ByteArrayInputStream(buffer), length); } blob.commitBlockList(blocks.values()); for (BlockEntry block : extraBlocks.values()) { blob.uploadBlock(block.getId(), new ByteArrayInputStream(buffer), length); } CloudBlockBlob blob2 = this.container.getBlockBlobReference(blobName); blob2.downloadAttributes(); assertEquals(1024 * blocks.size(), blob2.getProperties().getLength()); List<BlockEntry> blockList = blob2.downloadBlockList(); assertEquals(3, blockList.size()); for (BlockEntry blockItem : blockList) { assertEquals(BlockSearchMode.COMMITTED, blockItem.getSearchMode()); assertEquals(length, blockItem.getSize()); assertFalse(blocks.remove(blockItem.getId()) == null); } assertEquals(0, blocks.size()); blockList = blob2.downloadBlockList(BlockListingFilter.UNCOMMITTED, null, null, null); assertEquals(2, blockList.size()); for (BlockEntry blockItem : blockList) { assertEquals(BlockSearchMode.UNCOMMITTED, blockItem.getSearchMode()); assertEquals(length, blockItem.getSize()); assertFalse(extraBlocks.remove(blockItem.getId()) == null); } assertEquals(0, extraBlocks.size()); blockList = blob2.downloadBlockList(BlockListingFilter.ALL, null, null, null); assertEquals(5, blockList.size()); } @Test @Category({ DevFabricTests.class, DevStoreTests.class }) public void testBlockBlobDownloadRangeTest() throws URISyntaxException, StorageException, IOException { byte[] buffer = BlobTestHelper.getRandomBuffer(2 * 1024); CloudBlockBlob blob = this.container.getBlockBlobReference("blob1"); ByteArrayInputStream wholeBlob = new ByteArrayInputStream(buffer); blob.upload(wholeBlob, -1); ByteArrayOutputStream blobStream = new ByteArrayOutputStream(); try { blob.downloadRange(0, new Long(0), blobStream, null, null, null); } catch (IndexOutOfBoundsException ex) { } blob.downloadRange(0, new Long(1024), blobStream); assertEquals(blobStream.size(), 1024); BlobTestHelper.assertStreamsAreEqualAtIndex(new ByteArrayInputStream(blobStream.toByteArray()), wholeBlob, 0, 0, 1024, 2 * 1024); CloudBlockBlob blob2 = this.container.getBlockBlobReference("blob1"); try { blob.downloadRange(1024, new Long(0), blobStream, null, null, null); } catch (IndexOutOfBoundsException ex) { } ByteArrayOutputStream blobStream2 = new ByteArrayOutputStream(); blob2.downloadRange(1024, new Long(1024), blobStream2); BlobTestHelper.assertStreamsAreEqualAtIndex(new ByteArrayInputStream(blobStream2.toByteArray()), wholeBlob, 0, 1024, 1024, 2 * 1024); blob2.downloadAttributes(); BlobTestHelper.assertAreEqual(blob, blob2); } @Test @Category({ DevFabricTests.class, DevStoreTests.class }) public void testBlobUploadFromStreamTest() throws URISyntaxException, StorageException, IOException { final String blockBlobName = BlobTestHelper.generateRandomBlobNameWithPrefix("testBlockBlob"); final CloudBlockBlob blockBlobRef = this.container.getBlockBlobReference(blockBlobName); int length = 2 * 1024; ByteArrayInputStream srcStream = BlobTestHelper.getRandomDataStream(length); blockBlobRef.upload(srcStream, -1); ByteArrayOutputStream dstStream = new ByteArrayOutputStream(); blockBlobRef.download(dstStream); BlobTestHelper.assertStreamsAreEqual(srcStream, new ByteArrayInputStream(dstStream.toByteArray())); length = 5 * Constants.MB; srcStream = BlobTestHelper.getRandomDataStream(length); blockBlobRef.upload(srcStream, length); dstStream = new ByteArrayOutputStream(); blockBlobRef.download(dstStream); BlobTestHelper.assertStreamsAreEqual(srcStream, new ByteArrayInputStream(dstStream.toByteArray())); } @Test public void testBlobUploadFromStreamAccessConditionTest() throws URISyntaxException, StorageException, IOException { final String blockBlobName = BlobTestHelper.generateRandomBlobNameWithPrefix("testBlockBlob"); final CloudBlockBlob blockBlobRef = this.container.getBlockBlobReference(blockBlobName); AccessCondition accessCondition = AccessCondition.generateIfNotModifiedSinceCondition(new Date()); int length = 2 * 1024; ByteArrayInputStream srcStream = BlobTestHelper.getRandomDataStream(length); blockBlobRef.upload(srcStream, -1, accessCondition, null, null); ByteArrayOutputStream dstStream = new ByteArrayOutputStream(); blockBlobRef.download(dstStream); BlobTestHelper.assertStreamsAreEqual(srcStream, new ByteArrayInputStream(dstStream.toByteArray())); length = 5 * Constants.MB; srcStream = BlobTestHelper.getRandomDataStream(length); blockBlobRef.upload(srcStream, length); dstStream = new ByteArrayOutputStream(); blockBlobRef.download(dstStream); BlobTestHelper.assertStreamsAreEqual(srcStream, new ByteArrayInputStream(dstStream.toByteArray())); } @Test @Category({ DevFabricTests.class, DevStoreTests.class }) public void testBlobUploadFromStreamRequestOptionsTest() throws URISyntaxException, StorageException, IOException { final String blockBlobName1 = BlobTestHelper.generateRandomBlobNameWithPrefix("testBlockBlob"); final CloudBlockBlob blockBlobRef1 = this.container.getBlockBlobReference(blockBlobName1); final String blockBlobName2 = BlobTestHelper.generateRandomBlobNameWithPrefix("testBlockBlob"); final CloudBlockBlob blockBlobRef2 = this.container.getBlockBlobReference(blockBlobName2); final int length = 2 * com.microsoft.azure.storage.Constants.MB; ByteArrayInputStream srcStream = BlobTestHelper.getRandomDataStream(length); BlobRequestOptions options = new BlobRequestOptions(); options.setSingleBlobPutThresholdInBytes(length / 2); options.setRetryPolicyFactory(RetryNoRetry.getInstance()); OperationContext context = new OperationContext(); blockBlobRef1.upload(srcStream, length, null /* accessCondition */, options, context); assertTrue(context.getRequestResults().size() >= 2); srcStream.reset(); options.setSingleBlobPutThresholdInBytes(length); context = new OperationContext(); blockBlobRef2.upload(srcStream, length, null /* accessCondition */, options, context); assertTrue(context.getRequestResults().size() <= 2); } @Test @Category({ DevFabricTests.class, DevStoreTests.class, SlowTests.class }) public void testLargeBlobUploadFromStreamTest() throws URISyntaxException, StorageException, IOException { final String blockBlobName = BlobTestHelper.generateRandomBlobNameWithPrefix("testBlockBlob"); final CloudBlockBlob blockBlobRef = this.container.getBlockBlobReference(blockBlobName); BlobRequestOptions options = new BlobRequestOptions(); options.setStoreBlobContentMD5(false); options.setEncryptionPolicy(null); int length = 30 * Constants.MB; options.setSingleBlobPutThresholdInBytes(length/2); ByteArrayInputStream srcStream = BlobTestHelper.getRandomDataStream(length); blockBlobRef.streamWriteSizeInBytes = 5 * Constants.MB; options.setConcurrentRequestCount(1); blockBlobRef.upload(srcStream, length ,null,options,null); ByteArrayOutputStream dstStream = new ByteArrayOutputStream(); blockBlobRef.download(dstStream); BlobTestHelper.assertStreamsAreEqual(srcStream, new ByteArrayInputStream(dstStream.toByteArray())); srcStream = BlobTestHelper.getRandomDataStream(length); options.setConcurrentRequestCount(3); blockBlobRef.upload(srcStream, length ,null,options,null); dstStream = new ByteArrayOutputStream(); blockBlobRef.download(dstStream); BlobTestHelper.assertStreamsAreEqual(srcStream, new ByteArrayInputStream(dstStream.toByteArray())); srcStream = BlobTestHelper.getRandomDataStream(length + 1); blockBlobRef.streamWriteSizeInBytes = 5 * Constants.MB + 1; options.setConcurrentRequestCount(1); blockBlobRef.upload(srcStream, length + 1, null, options, null); dstStream = new ByteArrayOutputStream(); blockBlobRef.download(dstStream); BlobTestHelper.assertStreamsAreEqual(srcStream, new ByteArrayInputStream(dstStream.toByteArray())); srcStream = BlobTestHelper.getRandomDataStream(length + 1); options.setConcurrentRequestCount(5); blockBlobRef.upload(srcStream, length + 1, null, options, null); dstStream = new ByteArrayOutputStream(); blockBlobRef.download(dstStream); BlobTestHelper.assertStreamsAreEqual(srcStream, new ByteArrayInputStream(dstStream.toByteArray())); } @Test @Category({ DevFabricTests.class, DevStoreTests.class, SlowTests.class }) public void testLargeSinglePutBlobTest() throws URISyntaxException, StorageException, IOException { final String blockBlobName = BlobTestHelper.generateRandomBlobNameWithPrefix("testBlockBlob"); final String blockBlobName2 = BlobTestHelper.generateRandomBlobNameWithPrefix("testBlockBlob"); final String blockBlobName3 = BlobTestHelper.generateRandomBlobNameWithPrefix("testBlockBlob"); final CloudBlockBlob blob = this.container.getBlockBlobReference(blockBlobName); final CloudBlockBlob blob2 = this.container.getBlockBlobReference(blockBlobName2); final CloudBlockBlob blob3 = this.container.getBlockBlobReference(blockBlobName3); BlobRequestOptions options = new BlobRequestOptions(); options.setStoreBlobContentMD5(false); options.setEncryptionPolicy(null); try { byte[] buffer = BlobTestHelper.getRandomBuffer(256 * Constants.MB); OperationContext operationContext = new OperationContext(); blob.uploadFromByteArray(buffer, 0, 128 * Constants.MB, null, null, operationContext); assertEquals(1, operationContext.getRequestResults().size()); options.setSingleBlobPutThresholdInBytes(256 * Constants.MB); blob2.uploadFromByteArray(buffer, 0, 256 * Constants.MB, null, options, operationContext); assertEquals(1, operationContext.getRequestResults().size()); // Reduce threshold and upload data greater than the single put blob upload threshold options.setSingleBlobPutThresholdInBytes(Constants.MB); blob3.uploadFromByteArray(buffer, 0, 3 * Constants.MB, null, options, operationContext); assertTrue(operationContext.getRequestResults().size() > 1); } finally { blob.delete(); blob2.delete(); blob3.delete(); } } @Test @Category({ DevFabricTests.class, DevStoreTests.class }) public void testLargeBlobUploadFromStreamAccessConditionTest() throws URISyntaxException, StorageException, IOException { final String blockBlobName = BlobTestHelper.generateRandomBlobNameWithPrefix("testBlockBlob"); final CloudBlockBlob blockBlobRef = this.container.getBlockBlobReference(blockBlobName); AccessCondition accessCondition = AccessCondition.generateIfNotModifiedSinceCondition(new Date()); BlobRequestOptions options = new BlobRequestOptions(); int length = 30 * Constants.MB; blockBlobRef.setStreamWriteSizeInBytes(7 * Constants.MB); options.setConcurrentRequestCount(3); options.setStoreBlobContentMD5(false); options.setSingleBlobPutThresholdInBytes(length/2); ByteArrayInputStream srcStream = BlobTestHelper.getRandomDataStream(length); blockBlobRef.upload(srcStream, length, accessCondition, options, null); ByteArrayOutputStream dstStream = new ByteArrayOutputStream(); blockBlobRef.download(dstStream); BlobTestHelper.assertStreamsAreEqual(srcStream, new ByteArrayInputStream(dstStream.toByteArray())); try { srcStream = BlobTestHelper.getRandomDataStream(length); blockBlobRef.upload(srcStream, length, accessCondition, options, null); } catch (StorageException ex) { assertEquals(412, ex.getHttpStatusCode()); assertEquals(StorageErrorCodeStrings.CONDITION_NOT_MET, ex.getErrorCode()); } } @Test @Category({ DevFabricTests.class, DevStoreTests.class }) public void testLargeBlobUploadFromStreamRequestOptionsTest() throws URISyntaxException, StorageException, IOException { final String blockBlobName1 = BlobTestHelper.generateRandomBlobNameWithPrefix("testBlockBlob"); final CloudBlockBlob blockBlobRef1 = this.container.getBlockBlobReference(blockBlobName1); final String blockBlobName2 = BlobTestHelper.generateRandomBlobNameWithPrefix("testBlockBlob"); final CloudBlockBlob blockBlobRef2 = this.container.getBlockBlobReference(blockBlobName2); final int length = 24 * Constants.MB; ByteArrayInputStream srcStream = BlobTestHelper.getRandomDataStream(length); BlobRequestOptions options = new BlobRequestOptions(); options.setSingleBlobPutThresholdInBytes(length / 2); options.setRetryPolicyFactory(RetryNoRetry.getInstance()); options.setConcurrentRequestCount(3); options.setStoreBlobContentMD5(false); OperationContext context = new OperationContext(); blockBlobRef1.setStreamWriteSizeInBytes(4 * Constants.MB + 1 ); blockBlobRef1.upload(srcStream, length, null /* accessCondition */, options, context); assertTrue(context.getRequestResults().size() >= 7); srcStream.reset(); context = new OperationContext(); blockBlobRef2.setStreamWriteSizeInBytes(8 * Constants.MB); blockBlobRef2.upload(srcStream, length, null /* accessCondition */, options, context); assertTrue(context.getRequestResults().size() >= 4); } @Test @Category({ DevFabricTests.class, DevStoreTests.class }) public void testUploadDownloadBlobProperties() throws URISyntaxException, StorageException, IOException { final int length = 0; // do this to make sure the set MD5 can be compared without an exception being thrown BlobRequestOptions options = new BlobRequestOptions(); options.setDisableContentMD5Validation(true); // with explicit upload/download of properties String blockBlobName1 = BlobTestHelper.generateRandomBlobNameWithPrefix("testBlockBlob"); CloudBlockBlob blockBlobRef1 = this.container.getBlockBlobReference(blockBlobName1); blockBlobRef1.upload(BlobTestHelper.getRandomDataStream(length), length); // this is not set by upload (it is for page blob!), so set this manually blockBlobRef1.getProperties().setLength(length); BlobTestHelper.setBlobProperties(blockBlobRef1); BlobProperties props1 = blockBlobRef1.getProperties(); blockBlobRef1.uploadProperties(); blockBlobRef1.downloadAttributes(null, options, null); BlobProperties props2 = blockBlobRef1.getProperties(); assertEquals(props1.getLength(), props2.getLength()); BlobTestHelper.assertAreEqual(props1, props2); // by uploading/downloading the blob blockBlobName1 = BlobTestHelper.generateRandomBlobNameWithPrefix("testBlockBlob"); blockBlobRef1 = this.container.getBlockBlobReference(blockBlobName1); BlobTestHelper.setBlobProperties(blockBlobRef1); props1 = blockBlobRef1.getProperties(); blockBlobRef1.upload(BlobTestHelper.getRandomDataStream(length), length); blockBlobRef1.download(new ByteArrayOutputStream(), null, options, null); props2 = blockBlobRef1.getProperties(); BlobTestHelper.assertAreEqual(props1, props2); } @Test @Category({ DevFabricTests.class, DevStoreTests.class }) public void testBlobUploadWithoutMD5Validation() throws URISyntaxException, StorageException, IOException { final String blockBlobName = BlobTestHelper.generateRandomBlobNameWithPrefix("testBlockBlob"); final CloudBlockBlob blockBlobRef = this.container.getBlockBlobReference(blockBlobName); final int length = 2 * 1024; ByteArrayInputStream srcStream = BlobTestHelper.getRandomDataStream(length); BlobRequestOptions options = new BlobRequestOptions(); options.setDisableContentMD5Validation(false); options.setStoreBlobContentMD5(false); blockBlobRef.upload(srcStream, -1, null, options, null); blockBlobRef.downloadAttributes(); blockBlobRef.getProperties().setContentMD5("MDAwMDAwMDA="); blockBlobRef.uploadProperties(null, options, null); try { blockBlobRef.download(new ByteArrayOutputStream(), null, options, null); fail(); } catch (StorageException ex) { assertEquals(306, ex.getHttpStatusCode()); assertEquals("InvalidMd5", ex.getErrorCode()); } options.setDisableContentMD5Validation(true); blockBlobRef.download(new ByteArrayOutputStream(), null, options, null); final CloudBlockBlob blockBlobRef2 = this.container.getBlockBlobReference(blockBlobName); assertNull(blockBlobRef2.getProperties().getContentMD5()); byte[] target = new byte[4]; blockBlobRef2.downloadRangeToByteArray(0L, 4L, target, 0); assertEquals("MDAwMDAwMDA=", blockBlobRef2.properties.getContentMD5()); } @Test @Category({ DevFabricTests.class, DevStoreTests.class }) public void testVerifyTransactionalMD5ValidationMissingOverallMD5() throws URISyntaxException, StorageException, IOException { final String blockBlobName = BlobTestHelper.generateRandomBlobNameWithPrefix("testBlockBlob"); final CloudBlockBlob blockBlobRef = this.container.getBlockBlobReference(blockBlobName); final int length = 2 * 1024 * 1024; ByteArrayInputStream srcStream = BlobTestHelper.getRandomDataStream(length); BlobRequestOptions options = new BlobRequestOptions(); options.setSingleBlobPutThresholdInBytes(1024*1024); options.setDisableContentMD5Validation(true); options.setStoreBlobContentMD5(false); blockBlobRef.upload(srcStream, -1, null, options, null); options.setDisableContentMD5Validation(false); options.setStoreBlobContentMD5(true); options.setUseTransactionalContentMD5(true); final CloudBlockBlob blockBlobRef2 = this.container.getBlockBlobReference(blockBlobName); blockBlobRef2.downloadRange(1024, (long)1024, new ByteArrayOutputStream(), null, options, null); assertNull(blockBlobRef2.getProperties().getContentMD5()); } @Test @Category({ DevFabricTests.class, DevStoreTests.class }) public void testBlockBlobUploadContentMD5() throws URISyntaxException, StorageException, IOException { final String blockBlobName = BlobTestHelper.generateRandomBlobNameWithPrefix("testBlockBlob"); CloudBlockBlob blockBlobRef = this.container.getBlockBlobReference(blockBlobName); int length = 16 * 1024; ByteArrayInputStream srcStream = BlobTestHelper.getRandomDataStream(length); final ArrayList<Boolean> callList = new ArrayList<Boolean>(); OperationContext sendingRequestEventContext = new OperationContext(); StorageEvent<SendingRequestEvent> event = new StorageEvent<SendingRequestEvent>() { @Override public void eventOccurred(SendingRequestEvent eventArg) { assertNotNull(((HttpURLConnection) eventArg.getConnectionObject()) .getRequestProperty(BlobConstants.BLOB_CONTENT_MD5_HEADER)); callList.add(true); } }; sendingRequestEventContext.getSendingRequestEventHandler().addListener(event); assertEquals(0, callList.size()); // Upload with length less than single threshold. Make sure we calculate the contentMD5. blockBlobRef.upload(srcStream, length, null, null, sendingRequestEventContext); assertEquals(1, callList.size()); sendingRequestEventContext.getSendingRequestEventHandler().removeListener(event); callList.clear(); event = new StorageEvent<SendingRequestEvent>() { @Override public void eventOccurred(SendingRequestEvent eventArg) { callList.add(true); } }; length = BlobConstants.DEFAULT_SINGLE_BLOB_PUT_THRESHOLD_IN_BYTES + 1; srcStream = BlobTestHelper.getRandomDataStream(length); sendingRequestEventContext.getSendingRequestEventHandler().addListener(event); assertEquals(0, callList.size()); // Upload with length greater than single threshold. This will do multiple block uploads. blockBlobRef.upload(srcStream, length, null, null, sendingRequestEventContext); assertTrue(callList.size() > 1); } @Test @Category({ DevFabricTests.class, DevStoreTests.class }) public void testBlobEmptyHeaderSigningTest() throws URISyntaxException, StorageException, IOException { final String blockBlobName = BlobTestHelper.generateRandomBlobNameWithPrefix("testBlockBlob"); final CloudBlockBlob blockBlobRef = this.container.getBlockBlobReference(blockBlobName); final int length = 2 * 1024; ByteArrayInputStream srcStream = BlobTestHelper.getRandomDataStream(length); OperationContext context = new OperationContext(); context.getSendingRequestEventHandler().addListener(new StorageEvent<SendingRequestEvent>() { @Override public void eventOccurred(SendingRequestEvent eventArg) { HttpURLConnection connection = (HttpURLConnection) eventArg.getConnectionObject(); connection.setRequestProperty("x-ms-foo", ""); } }); blockBlobRef.upload(srcStream, -1, null, null, context); blockBlobRef.download(new ByteArrayOutputStream(), null, null, context); } @Test @Category({ DevFabricTests.class, DevStoreTests.class }) public void testCloudBlockBlobDownloadToByteArray() throws URISyntaxException, StorageException, IOException { CloudBlockBlob blob = this.container.getBlockBlobReference("blob1"); BlobTestHelper.doDownloadTest(blob, 1 * 512, 2 * 512, 0); BlobTestHelper.doDownloadTest(blob, 1 * 512, 2 * 512, 1 * 512); BlobTestHelper.doDownloadTest(blob, 2 * 512, 4 * 512, 1 * 512); BlobTestHelper.doDownloadTest(blob, 5 * Constants.MB, 5 * Constants.MB, 0); BlobTestHelper.doDownloadTest(blob, 5 * Constants.MB, 6 * Constants.MB, 512); } @Test @Category({ DevFabricTests.class, DevStoreTests.class }) public void testCloudBlockBlobDownloadRangeToByteArray() throws URISyntaxException, StorageException, IOException { CloudBlockBlob blob = this.container.getBlockBlobReference(BlobTestHelper .generateRandomBlobNameWithPrefix("downloadrange")); BlobTestHelper.doDownloadRangeToByteArrayTest(blob, 8 * Constants.MB, 8 * Constants.MB, 1 * Constants.MB, new Long(1 * Constants.MB), new Long(5 * Constants.MB)); BlobTestHelper.doDownloadRangeToByteArrayTest(blob, 8 * Constants.MB, 8 * Constants.MB, 2 * Constants.MB, new Long(2 * Constants.MB), new Long(6 * Constants.MB)); BlobTestHelper.doDownloadRangeToByteArrayTest(blob, 8 * Constants.MB, 8 * Constants.MB, 1 * Constants.MB, new Long(4 * Constants.MB), new Long(4 * Constants.MB)); BlobTestHelper.doDownloadRangeToByteArrayTest(blob, 2 * 512, 4 * 512, 0, new Long(1 * 512), new Long(1 * 512)); BlobTestHelper.doDownloadRangeToByteArrayTest(blob, 2 * 512, 4 * 512, 1 * 512, new Long(0), null); BlobTestHelper.doDownloadRangeToByteArrayTest(blob, 2 * 512, 4 * 512, 1 * 512, new Long(1 * 512), null); BlobTestHelper.doDownloadRangeToByteArrayTest(blob, 2 * 512, 4 * 512, 1 * 512, new Long(0), new Long(1 * 512)); BlobTestHelper.doDownloadRangeToByteArrayTest(blob, 2 * 512, 4 * 512, 2 * 512, new Long(1 * 512), new Long( 1 * 512)); BlobTestHelper.doDownloadRangeToByteArrayTest(blob, 2 * 512, 4 * 512, 2 * 512, new Long(1 * 512), new Long( 2 * 512)); // Edge cases BlobTestHelper.doDownloadRangeToByteArrayTest(blob, 1024, 1024, 1023, new Long(1023), new Long(1)); BlobTestHelper.doDownloadRangeToByteArrayTest(blob, 1024, 1024, 0, new Long(1023), new Long(1)); BlobTestHelper.doDownloadRangeToByteArrayTest(blob, 1024, 1024, 0, new Long(0), new Long(1)); BlobTestHelper.doDownloadRangeToByteArrayTest(blob, 1024, 1024, 0, new Long(512), new Long(1)); BlobTestHelper.doDownloadRangeToByteArrayTest(blob, 1024, 1024, 512, new Long(1023), new Long(1)); } @Test @Category({ DevFabricTests.class, DevStoreTests.class }) public void testCloudBlockBlobDownloadRangeToByteArrayNegativeTest() throws URISyntaxException, StorageException, IOException { CloudBlockBlob blob = this.container.getBlockBlobReference(BlobTestHelper .generateRandomBlobNameWithPrefix("downloadrangenegative")); BlobTestHelper.doDownloadRangeToByteArrayNegativeTests(blob); } @Test @Category({ DevFabricTests.class, DevStoreTests.class }) public void testCloudBlockBlobUploadFromStreamWithAccessCondition() throws URISyntaxException, StorageException, IOException { CloudBlockBlob blob1 = this.container.getBlockBlobReference("blob1"); AccessCondition accessCondition = AccessCondition.generateIfNoneMatchCondition("\"*\""); final int length = 2 * Constants.MB; ByteArrayInputStream srcStream = BlobTestHelper.getRandomDataStream(length); blob1.upload(srcStream, length, accessCondition, null, null); blob1.downloadAttributes(); CloudBlockBlob blob2 = (CloudBlockBlob) BlobTestHelper.uploadNewBlob(this.container, BlobType.BLOCK_BLOB, "accesscond", length, null); blob2.downloadAttributes(); srcStream.reset(); accessCondition = AccessCondition.generateIfNoneMatchCondition(blob1.getProperties().getEtag()); try { blob1.upload(srcStream, length, accessCondition, null, null); } catch (StorageException ex) { assertEquals(HttpURLConnection.HTTP_PRECON_FAILED, ex.getHttpStatusCode()); } srcStream.reset(); accessCondition = AccessCondition.generateIfMatchCondition(blob1.getProperties().getEtag()); blob1.upload(srcStream, length, accessCondition, null, null); srcStream.reset(); accessCondition = AccessCondition.generateIfMatchCondition(blob2.getProperties().getEtag()); try { blob1.upload(srcStream, length, accessCondition, null, null); } catch (StorageException ex) { assertEquals(HttpURLConnection.HTTP_PRECON_FAILED, ex.getHttpStatusCode()); } srcStream.reset(); accessCondition = AccessCondition.generateIfNoneMatchCondition(blob2.getProperties().getEtag()); blob1.upload(srcStream, length, accessCondition, null, null); } /** * @throws StorageException * @throws URISyntaxException * @throws IOException * @throws InterruptedException */ @Test @Category({ DevFabricTests.class, DevStoreTests.class, SlowTests.class }) public void testBlobNamePlusEncodingTest() throws StorageException, URISyntaxException, IOException, InterruptedException { final int length = 1 * 1024; final CloudBlockBlob originalBlob = (CloudBlockBlob) BlobTestHelper.uploadNewBlob(this.container, BlobType.BLOCK_BLOB, "a+b.txt", length, null); final CloudBlockBlob copyBlob = this.container.getBlockBlobReference(originalBlob.getName() + "copyed"); copyBlob.startCopy(originalBlob); BlobTestHelper.waitForCopy(copyBlob); copyBlob.downloadAttributes(); } /** * @throws StorageException * @throws URISyntaxException * @throws IOException * @throws InterruptedException */ @Test @Category({ DevFabricTests.class, DevStoreTests.class }) public void testSendingRequestEventBlob() throws StorageException, URISyntaxException, IOException { final int length = 128; final ArrayList<Boolean> callList = new ArrayList<Boolean>(); OperationContext sendingRequestEventContext = new OperationContext(); sendingRequestEventContext.getSendingRequestEventHandler().addListener(new StorageEvent<SendingRequestEvent>() { @Override public void eventOccurred(SendingRequestEvent eventArg) { assertEquals(eventArg.getRequestResult(), eventArg.getOpContext().getLastResult()); callList.add(true); } }); assertEquals(0, callList.size()); //Put blob CloudBlob blob = BlobTestHelper.uploadNewBlob(this.container, BlobType.BLOCK_BLOB, "bb", length, sendingRequestEventContext); assertEquals(1, callList.size()); //Get blob blob.download(new ByteArrayOutputStream(), null, null, sendingRequestEventContext); assertEquals(2, callList.size()); //uploadMetadata blob.uploadMetadata(null, null, sendingRequestEventContext); assertEquals(3, callList.size()); //uploadMetadata blob.downloadAttributes(null, null, sendingRequestEventContext); assertEquals(4, callList.size()); } /** * @throws URISyntaxException * @throws StorageException * @throws IOException */ @Test @Category({ DevFabricTests.class, DevStoreTests.class }) public void testBlobInputStream() throws URISyntaxException, StorageException, IOException { final int blobLength = 16 * 1024; final Random randGenerator = new Random(); String blobName = BlobTestHelper.generateRandomBlobNameWithPrefix("testblob"); final CloudBlockBlob blobRef = this.container.getBlockBlobReference(blobName); final byte[] buff = new byte[blobLength]; randGenerator.nextBytes(buff); buff[0] = -1; buff[1] = -128; final ByteArrayInputStream sourceStream = new ByteArrayInputStream(buff); final BlobRequestOptions options = new BlobRequestOptions(); final OperationContext operationContext = new OperationContext(); options.setStoreBlobContentMD5(true); options.setTimeoutIntervalInMs(90000); options.setRetryPolicyFactory(new RetryNoRetry()); blobRef.uploadFullBlob(sourceStream, blobLength, null, options, operationContext); BlobInputStream blobStream = blobRef.openInputStream(); for (int i = 0; i < blobLength; i++) { int data = blobStream.read(); assertTrue(data >= 0); assertEquals(buff[i], (byte) data); } assertEquals(-1, blobStream.read()); blobRef.delete(); } @Test @Category({ DevFabricTests.class, DevStoreTests.class }) public void testUploadFromByteArray() throws Exception { String blobName = BlobTestHelper.generateRandomBlobNameWithPrefix("testblob"); final CloudBlockBlob blob = this.container.getBlockBlobReference(blobName); this.doUploadFromByteArrayTest(blob, 4 * 512, 0, 4 * 512); this.doUploadFromByteArrayTest(blob, 4 * 512, 0, 2 * 512); this.doUploadFromByteArrayTest(blob, 4 * 512, 1 * 512, 2 * 512); this.doUploadFromByteArrayTest(blob, 4 * 512, 2 * 512, 2 * 512); this.doUploadFromByteArrayTest(blob, 512, 0, 511); } private void doUploadFromByteArrayTest(CloudBlockBlob blob, int bufferSize, int bufferOffset, int count) throws Exception { byte[] buffer = BlobTestHelper.getRandomBuffer(bufferSize); byte[] downloadedBuffer = new byte[bufferSize]; blob.uploadFromByteArray(buffer, bufferOffset, count); blob.downloadToByteArray(downloadedBuffer, 0); int i = 0; for (; i < count; i++) { assertEquals(buffer[i + bufferOffset], downloadedBuffer[i]); } for (; i < downloadedBuffer.length; i++) { assertEquals(0, downloadedBuffer[i]); } } @Test @Category({ DevFabricTests.class, DevStoreTests.class }) public void testUploadDownloadFromFile() throws IOException, StorageException, URISyntaxException { String blobName = BlobTestHelper.generateRandomBlobNameWithPrefix("testblob"); final CloudBlockBlob blob = this.container.getBlockBlobReference(blobName); this.doUploadDownloadFileTest(blob, 0); this.doUploadDownloadFileTest(blob, 4096); this.doUploadDownloadFileTest(blob, 4097); } @Test @Category({ DevFabricTests.class, DevStoreTests.class, SlowTests.class }) public void testLargeUploadDownloadFromFile() throws IOException, StorageException, URISyntaxException { String blobName = BlobTestHelper.generateRandomBlobNameWithPrefix("testblob"); final CloudBlockBlob blob = this.container.getBlockBlobReference(blobName); byte[] buffer = BlobTestHelper.getRandomBuffer(30 * Constants.MB); this.doUploadDownloadFileTest(blob, buffer, 5 * Constants.MB, 1, false, false); this.doUploadDownloadFileTest(blob, buffer, 5 * Constants.MB, 5, false, false); this.doUploadDownloadFileTest(blob, buffer, 7 * Constants.MB, 1, false, false); this.doUploadDownloadFileTest(blob, buffer, 7 * Constants.MB, 4, false, false); this.doUploadDownloadFileTest(blob, buffer, 20 * Constants.MB + 1, 1, false, false); this.doUploadDownloadFileTest(blob, buffer, 20 * Constants.MB + 1, 2, false, false); } private void doUploadDownloadFileTest(CloudBlockBlob blob, int fileSize) throws IOException, StorageException { this.doUploadDownloadFileTest(blob, BlobTestHelper.getRandomBuffer(fileSize), blob.getStreamWriteSizeInBytes(), 1, true, true); } private void doUploadDownloadFileTest(CloudBlockBlob blob, byte[] fileBuffer, int blockSize, int parallelThreads, boolean useSingleBlobThreshold, boolean storeBlobContentMD5 ) throws IOException, StorageException { File sourceFile = File.createTempFile("sourceFile", ".tmp"); File destinationFile = new File(sourceFile.getParentFile(), "destinationFile.tmp"); try { FileOutputStream fos = new FileOutputStream(sourceFile); fos.write(fileBuffer); fos.close(); BlobRequestOptions options = new BlobRequestOptions(); options.setConcurrentRequestCount(parallelThreads); options.setStoreBlobContentMD5(storeBlobContentMD5); if (!useSingleBlobThreshold) { options.setSingleBlobPutThresholdInBytes(fileBuffer.length/2); } blob.setStreamWriteSizeInBytes( blockSize); blob.uploadFromFile(sourceFile.getAbsolutePath(), null, options, null); assertTrue("Destination file exists.", !destinationFile.exists()); blob.downloadToFile(destinationFile.getAbsolutePath()); assertTrue("Destination file does not exist.", destinationFile.exists()); assertEquals("Destination file length does not match input file.", fileBuffer.length, destinationFile.length()); FileInputStream fis = new FileInputStream(destinationFile); byte[] readBuffer = new byte[fileBuffer.length]; fis.read(readBuffer); fis.close(); for (int i = 0; i < fileBuffer.length; i++) { assertEquals("File contents do not match.", fileBuffer[i], readBuffer[i]); } } finally { if (sourceFile.exists()) { sourceFile.delete(); } if (destinationFile.exists()) { destinationFile.delete(); } } } @Test @Category({ DevFabricTests.class, DevStoreTests.class }) public void testUploadDownloadFromText() throws URISyntaxException, StorageException, IOException { String blobName = BlobTestHelper.generateRandomBlobNameWithPrefix("testblob"); final CloudBlockBlob blob = this.container.getBlockBlobReference(blobName); this.doUploadDownloadStringTest(blob, 0); this.doUploadDownloadStringTest(blob, 8000); } @Test @Category({ DevFabricTests.class, DevStoreTests.class }) public void testBlobMultiConditionHeaders() throws URISyntaxException, StorageException, IOException { final String blockBlobName = BlobTestHelper.generateRandomBlobNameWithPrefix("testBlockBlob"); final CloudBlockBlob blockBlobRef = this.container.getBlockBlobReference(blockBlobName); final int length = 2 * 1024; ByteArrayInputStream srcStream = BlobTestHelper.getRandomDataStream(length); OperationContext context = new OperationContext(); blockBlobRef.upload(srcStream, -1, null, null, context); AccessCondition condition = AccessCondition.generateIfMatchCondition(context.getLastResult().getEtag()); condition.setIfUnmodifiedSinceDate(context.getLastResult().getStartDate()); StorageEvent<SendingRequestEvent> event = new StorageEvent<SendingRequestEvent>() { @Override public void eventOccurred(SendingRequestEvent eventArg) { HttpURLConnection connection = (HttpURLConnection) eventArg.getConnectionObject(); assertNotNull(connection.getRequestProperty("If-Unmodified-Since")); assertNotNull(connection.getRequestProperty("If-Match")); } }; context.getSendingRequestEventHandler().addListener(event); blockBlobRef.upload(srcStream, -1, condition, null, context); } @Test @Category({ DevFabricTests.class, DevStoreTests.class }) public void testBlobConditionalAccess() throws StorageException, IOException, URISyntaxException { CloudBlockBlob blob = (CloudBlockBlob) BlobTestHelper.uploadNewBlob(this.container, BlobType.BLOCK_BLOB, "test", 128, null); blob.downloadAttributes(); String currentETag = blob.getProperties().getEtag(); Date currentModifiedTime = blob.getProperties().getLastModified(); // ETag conditional tests blob.getMetadata().put("ETagConditionalName", "ETagConditionalValue"); blob.uploadMetadata(AccessCondition.generateIfMatchCondition(currentETag), null, null); blob.downloadAttributes(); String newETag = blob.getProperties().getEtag(); assertFalse(newETag.equals(currentETag)); blob.getMetadata().put("ETagConditionalName", "ETagConditionalValue2"); try { blob.uploadMetadata(AccessCondition.generateIfNoneMatchCondition(newETag), null, null); fail("If none match on conditional test should throw"); } catch (StorageException e) { assertEquals("ConditionNotMet", e.getErrorCode()); assertEquals(HttpURLConnection.HTTP_PRECON_FAILED, e.getHttpStatusCode()); assertEquals("The condition specified using HTTP conditional header(s) is not met.", e.getMessage()); } String invalidETag = "\"0x10101010\""; try { blob.uploadMetadata(AccessCondition.generateIfMatchCondition(invalidETag), null, null); fail("Invalid ETag on conditional test should throw"); } catch (StorageException e) { assertEquals("ConditionNotMet", e.getErrorCode()); assertEquals(HttpURLConnection.HTTP_PRECON_FAILED, e.getHttpStatusCode()); assertEquals("The condition specified using HTTP conditional header(s) is not met.", e.getMessage()); } currentETag = blob.getProperties().getEtag(); blob.uploadMetadata(AccessCondition.generateIfNoneMatchCondition(invalidETag), null, null); blob.downloadAttributes(); newETag = blob.getProperties().getEtag(); // LastModifiedTime tests currentModifiedTime = blob.getProperties().getLastModified(); blob.getMetadata().put("DateConditionalName", "DateConditionalValue"); try { blob.uploadMetadata(AccessCondition.generateIfModifiedSinceCondition(currentModifiedTime), null, null); fail("IfModifiedSince conditional on current modified time should throw"); } catch (StorageException e) { assertEquals("ConditionNotMet", e.getErrorCode()); assertEquals(HttpURLConnection.HTTP_PRECON_FAILED, e.getHttpStatusCode()); assertEquals("The condition specified using HTTP conditional header(s) is not met.", e.getMessage()); } Calendar cal = Calendar.getInstance(); cal.setTime(currentModifiedTime); cal.set(Calendar.MINUTE, cal.get(Calendar.MINUTE) - 5); Date pastTime = cal.getTime(); blob.uploadMetadata(AccessCondition.generateIfModifiedSinceCondition(pastTime), null, null); cal = Calendar.getInstance(); cal.setTime(currentModifiedTime); cal.set(Calendar.HOUR, cal.get(Calendar.HOUR) - 5); pastTime = cal.getTime(); blob.uploadMetadata(AccessCondition.generateIfModifiedSinceCondition(pastTime), null, null); cal = Calendar.getInstance(); cal.setTime(currentModifiedTime); cal.set(Calendar.DAY_OF_MONTH, cal.get(Calendar.DAY_OF_MONTH) - 5); pastTime = cal.getTime(); blob.uploadMetadata(AccessCondition.generateIfModifiedSinceCondition(pastTime), null, null); currentModifiedTime = blob.getProperties().getLastModified(); cal = Calendar.getInstance(); cal.setTime(currentModifiedTime); cal.set(Calendar.MINUTE, cal.get(Calendar.MINUTE) - 5); pastTime = cal.getTime(); try { blob.uploadMetadata(AccessCondition.generateIfNotModifiedSinceCondition(pastTime), null, null); fail("IfNotModifiedSince conditional on past time should throw"); } catch (StorageException e) { assertEquals("ConditionNotMet", e.getErrorCode()); assertEquals(HttpURLConnection.HTTP_PRECON_FAILED, e.getHttpStatusCode()); assertEquals("The condition specified using HTTP conditional header(s) is not met.", e.getMessage()); } cal = Calendar.getInstance(); cal.setTime(currentModifiedTime); cal.set(Calendar.HOUR, cal.get(Calendar.HOUR) - 5); pastTime = cal.getTime(); try { blob.uploadMetadata(AccessCondition.generateIfNotModifiedSinceCondition(pastTime), null, null); fail("IfNotModifiedSince conditional on past time should throw"); } catch (StorageException e) { assertEquals("ConditionNotMet", e.getErrorCode()); assertEquals(HttpURLConnection.HTTP_PRECON_FAILED, e.getHttpStatusCode()); assertEquals("The condition specified using HTTP conditional header(s) is not met.", e.getMessage()); } cal = Calendar.getInstance(); cal.setTime(currentModifiedTime); cal.set(Calendar.DAY_OF_MONTH, cal.get(Calendar.DAY_OF_MONTH) - 5); pastTime = cal.getTime(); try { blob.uploadMetadata(AccessCondition.generateIfNotModifiedSinceCondition(pastTime), null, null); fail("IfNotModifiedSince conditional on past time should throw"); } catch (StorageException e) { assertEquals("ConditionNotMet", e.getErrorCode()); assertEquals(HttpURLConnection.HTTP_PRECON_FAILED, e.getHttpStatusCode()); assertEquals("The condition specified using HTTP conditional header(s) is not met.", e.getMessage()); } blob.getMetadata().put("DateConditionalName", "DateConditionalValue2"); currentETag = blob.getProperties().getEtag(); blob.uploadMetadata(AccessCondition.generateIfNotModifiedSinceCondition(currentModifiedTime), null, null); blob.downloadAttributes(); newETag = blob.getProperties().getEtag(); assertFalse("ETage should be modified on write metadata", newETag.equals(currentETag)); } @Test public void testBlobExceedMaxRange() throws URISyntaxException, StorageException, IOException { CloudBlockBlob blob = container.getBlockBlobReference("blockblob4"); blob.deleteIfExists(); byte[] msg = "my message".getBytes("UTF-8"); blob.uploadFromByteArray(msg, 0, msg.length); byte[] buffer = new byte[msg.length + 5]; blob.downloadRangeToByteArray(0, (long) buffer.length, buffer, 0, null, null, null); String expected = new String (msg, "UTF-8"); String actual = new String(buffer, "UTF-8").substring(0, 10); assertEquals(expected, actual); } @Test @Category({ DevFabricTests.class, DevStoreTests.class }) public void testBlobGetRangeContentMD5Bounds() throws StorageException, IOException, URISyntaxException { { CloudBlockBlob blob = (CloudBlockBlob) BlobTestHelper.uploadNewBlob(this.container, BlobType.BLOCK_BLOB, "test", 5 * Constants.MB, null); BlobRequestOptions options = new BlobRequestOptions(); OperationContext opContext = new OperationContext(); try { BlobRequest.getBlob(blob.getUri(), options, opContext, null, "", 0L, 4L * Constants.MB, true); BlobRequest.getBlob(blob.getUri(), options, opContext, null, "", 0L, 4L * Constants.MB + 1, true); fail("The request for range ContentMD5 should have thrown an Exception for exceeding the limit."); } catch (IllegalArgumentException e) { assertEquals(e.getMessage(), String.format("The value of the parameter 'count' should be between 1 and %1d.", Constants.MAX_RANGE_CONTENT_MD5)); } } } private void doUploadDownloadStringTest(CloudBlockBlob blob, int length) throws StorageException, IOException { String stringToUse = this.getRandomUNCString(length); blob.uploadText(stringToUse, Constants.UTF8_CHARSET, null, null, null); String newString = blob.downloadText(Constants.UTF8_CHARSET, null, null, null); assertEquals("Strings are not equal", stringToUse, newString); stringToUse = this.getRandomUNCString(length); blob.uploadText(stringToUse, "UTF-16", null, null, null); newString = blob.downloadText("UTF-16", null, null, null); assertEquals("Strings are not equal", stringToUse, newString); stringToUse = this.getRandomUNCString(length); blob.uploadText(stringToUse, "UTF-16BE", null, null, null); newString = blob.downloadText("UTF-16BE", null, null, null); assertEquals("Strings are not equal", stringToUse, newString); stringToUse = this.getRandomUNCString(length); blob.uploadText(stringToUse, "UTF-16LE", null, null, null); newString = blob.downloadText("UTF-16LE", null, null, null); assertEquals("Strings are not equal", stringToUse, newString); stringToUse = this.getRandomASCIIString(length); blob.uploadText(stringToUse, "US-ASCII", null, null, null); newString = blob.downloadText("US-ASCII", null, null, null); assertEquals("Strings are not equal", stringToUse, newString); } // Not a good test over all of Unicode, but good enough for our purposes private String getRandomUNCString(int length) { return this.getRandomString(length, 0xD7FF); } private String getRandomASCIIString(int length) { return this.getRandomString(length, 0x7F); } private String getRandomString(int length, int maxCodePoint) { int[] codePoints = new int[length]; Random random = new Random(237); for (int i = 0; i < length; i++) { codePoints[i] = random.nextInt(maxCodePoint); } return new String(codePoints, 0, length); } private void doCloudBlockBlobCopy(boolean sourceIsSas, boolean destinationIsSas) throws URISyntaxException, StorageException, IOException, InvalidKeyException, InterruptedException { // Create source on server. CloudBlockBlob source = this.container.getBlockBlobReference("source"); String data = "String data"; source.uploadText(data, Constants.UTF8_CHARSET, null, null, null); source.getMetadata().put("Test", "value"); source.uploadMetadata(); // Get destination reference CloudBlockBlob destination = this.container.getBlockBlobReference("destination"); destination.commitBlockList(new ArrayList<BlockEntry>()); CloudBlockBlob copySource = source; CloudBlockBlob copyDestination = destination; Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("UTC")); cal.setTime(new Date()); cal.add(Calendar.SECOND, 300); if (sourceIsSas) { // Source SAS must have read permissions SharedAccessBlobPolicy policy = new SharedAccessBlobPolicy(); policy.setPermissions(EnumSet.of(SharedAccessBlobPermissions.READ, SharedAccessBlobPermissions.WRITE)); policy.setSharedAccessExpiryTime(cal.getTime()); BlobContainerPermissions perms = new BlobContainerPermissions(); perms.getSharedAccessPolicies().put("read", policy); this.container.uploadPermissions(perms); Thread.sleep(30000); String sasToken = source.generateSharedAccessSignature(policy, null); // Get source BlockBlob reference StorageCredentialsSharedAccessSignature credentials = new StorageCredentialsSharedAccessSignature(sasToken); copySource = new CloudBlockBlob(credentials.transformUri(source.getUri())); } if (destinationIsSas) { // Destination SAS must have write permissions SharedAccessBlobPolicy policy = new SharedAccessBlobPolicy(); policy.setPermissions(EnumSet.of(SharedAccessBlobPermissions.READ, SharedAccessBlobPermissions.WRITE)); policy.setSharedAccessExpiryTime(cal.getTime()); BlobContainerPermissions perms = new BlobContainerPermissions(); // Source container must be public if source is not SAS if (!sourceIsSas) { perms.setPublicAccess(BlobContainerPublicAccessType.BLOB); } perms.getSharedAccessPolicies().put("write", policy); this.container.uploadPermissions(perms); Thread.sleep(30000); String sasToken = destination.generateSharedAccessSignature(policy, null); // Get destination block blob reference StorageCredentialsSharedAccessSignature credentials = new StorageCredentialsSharedAccessSignature(sasToken); copyDestination = new CloudBlockBlob(credentials.transformUri(destination.getUri())); } Thread.sleep(30000); // Start copy and wait for completion String copyId = copyDestination.startCopy(copySource); BlobTestHelper.waitForCopy(copyDestination); Calendar calendar = Calendar.getInstance(Utility.UTC_ZONE); destination.downloadAttributes(); // Check original blob references for equality assertEquals(CopyStatus.SUCCESS, destination.getCopyState().getStatus()); assertEquals(source.getSnapshotQualifiedUri().getPath(), destination.getCopyState().getSource().getPath()); assertEquals(data.length(), destination.getCopyState().getTotalBytes().intValue()); assertEquals(data.length(), destination.getCopyState().getBytesCopied().intValue()); assertEquals(copyId, destination.getProperties().getCopyState().getCopyId()); assertTrue(destination.getCopyState().getCompletionTime() .compareTo(new Date(calendar.get(Calendar.MINUTE) - 1)) > 0); if (!destinationIsSas) { try { copyDestination.abortCopy(destination.getCopyState().getCopyId()); } catch (StorageException ex) { assertEquals(HttpURLConnection.HTTP_CONFLICT, ex.getHttpStatusCode()); } } source.downloadAttributes(); assertNotNull(destination.getProperties().getEtag()); assertFalse(source.getProperties().getEtag().equals(destination.getProperties().getEtag())); assertTrue(destination.getProperties().getLastModified().compareTo(new Date(calendar.get(Calendar.MINUTE) - 1)) > 0); String copyData = destination.downloadText(Constants.UTF8_CHARSET, null, null, null); assertEquals(data, copyData); BlobProperties prop1 = destination.getProperties(); BlobProperties prop2 = source.getProperties(); assertEquals(prop1.getCacheControl(), prop2.getCacheControl()); assertEquals(prop1.getContentEncoding(), prop2.getContentEncoding()); assertEquals(prop1.getContentLanguage(), prop2.getContentLanguage()); assertEquals(prop1.getContentMD5(), prop2.getContentMD5()); assertEquals(prop1.getContentType(), prop2.getContentType()); assertEquals("value", destination.getMetadata().get("Test")); destination.delete(); source.delete(); } }