From 73185b326d69e984116d0621363b256bc4cc37f6 Mon Sep 17 00:00:00 2001 From: Joost de Nijs Date: Wed, 28 Mar 2012 14:06:59 -0700 Subject: [PATCH 01/17] Removing an unneeded dependency on javax.mail --- microsoft-azure-api/pom.xml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/microsoft-azure-api/pom.xml b/microsoft-azure-api/pom.xml index c83d5dcc1a333..16c7471ac7f8e 100644 --- a/microsoft-azure-api/pom.xml +++ b/microsoft-azure-api/pom.xml @@ -80,11 +80,6 @@ commons-logging 1.1.1 - - javax.mail - mail - 1.4 - org.apache.commons commons-lang3 From bdcefe192282c3e333f8a237d92a167ab8871275 Mon Sep 17 00:00:00 2001 From: "Justin Yu (Microsoft)" Date: Wed, 8 Aug 2012 02:00:11 -0700 Subject: [PATCH 02/17] Allow the endpoint ending with / --- .../services/core/storage/utils/Utility.java | 9 +++-- .../queue/client/CloudQueueTests.java | 34 ++++++++++++++++++- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/core/storage/utils/Utility.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/core/storage/utils/Utility.java index 5f746e6835968..d290b1ec3a47e 100644 --- a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/core/storage/utils/Utility.java +++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/core/storage/utils/Utility.java @@ -365,11 +365,16 @@ public static XMLStreamReader createXMLStreamReaderFromReader(final Reader reade * @return true if the specified URI is path-style; otherwise, false. */ public static boolean determinePathStyleFromUri(final URI baseURI, final String knownAccountName) { + String path = baseURI.getPath(); + if (knownAccountName == null) { - return !Utility.isNullOrEmpty(baseURI.getPath()); + if (Utility.isNullOrEmpty(path) || path.equals("/")) { + return false; + } + + return true; } - String path = baseURI.getPath(); if (!Utility.isNullOrEmpty(path) && path.startsWith("/")) { path = path.substring(1); } diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/queue/client/CloudQueueTests.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/queue/client/CloudQueueTests.java index 156474bf037f8..a8ec75d631c01 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/queue/client/CloudQueueTests.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/queue/client/CloudQueueTests.java @@ -98,8 +98,9 @@ public void testQueueSAS() throws StorageException, URISyntaxException, InvalidK // Add a policy, check setting and getting. SharedAccessQueuePolicy policy1 = new SharedAccessQueuePolicy(); Calendar now = GregorianCalendar.getInstance(); + now.add(Calendar.MINUTE, -15); policy1.setSharedAccessStartTime(now.getTime()); - now.add(Calendar.MINUTE, 10); + now.add(Calendar.MINUTE, 30); policy1.setSharedAccessExpiryTime(now.getTime()); String identifier = UUID.randomUUID().toString(); @@ -1270,4 +1271,35 @@ public void testQueueUpdateMetaData() throws URISyntaxException, StorageExceptio queue.setMetadata(metadata); queue.uploadMetadata(); } + + @Test + public void testSASClientParse() throws StorageException, URISyntaxException, InvalidKeyException { + + // Add a policy, check setting and getting. + SharedAccessQueuePolicy policy1 = new SharedAccessQueuePolicy(); + Calendar now = GregorianCalendar.getInstance(); + now.add(Calendar.MINUTE, -15); + policy1.setSharedAccessStartTime(now.getTime()); + now.add(Calendar.MINUTE, 30); + policy1.setSharedAccessExpiryTime(now.getTime()); + + policy1.setPermissions(EnumSet.of(SharedAccessQueuePermissions.READ, + SharedAccessQueuePermissions.PROCESSMESSAGES, SharedAccessQueuePermissions.ADD, + SharedAccessQueuePermissions.UPDATE)); + + String sasString = queue.generateSharedAccessSignature(policy1, null); + + URI queueUri = new URI("http://myaccount.queue.core.windows.net/myqueue"); + + CloudQueueClient queueClient1 = new CloudQueueClient(new URI("http://myaccount.queue.core.windows.net/"), + new StorageCredentialsSharedAccessSignature(sasString)); + + CloudQueue queue1 = new CloudQueue(queueUri, queueClient1); + queue1.getName(); + + CloudQueueClient queueClient2 = new CloudQueueClient(new URI("http://myaccount.queue.core.windows.net/"), + new StorageCredentialsSharedAccessSignature(sasString)); + CloudQueue queue2 = new CloudQueue(queueUri, queueClient2); + queue2.getName(); + } } From 5d8e21e375d1dfca04d2ff7850c7892f89ec584b Mon Sep 17 00:00:00 2001 From: "Justin Yu (Microsoft)" Date: Tue, 21 Aug 2012 13:45:35 -0700 Subject: [PATCH 03/17] Java bug fixes. --- .../services/blob/client/CloudBlob.java | 16 ++++ .../services/queue/client/CloudQueue.java | 5 +- .../services/table/client/AtomPubParser.java | 4 +- .../table/client/QueryTableOperation.java | 2 +- .../blob/client/CloudBlobContainerTests.java | 78 +++++++++++++++++++ .../queue/client/CloudQueueTests.java | 78 ++++++++++++++++++- .../table/client/TableClientTests.java | 17 +++- .../table/client/TableSerializerTests.java | 19 +++++ 8 files changed, 211 insertions(+), 8 deletions(-) diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/client/CloudBlob.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/client/CloudBlob.java index a434b75f3de7b..63043cae64301 100644 --- a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/client/CloudBlob.java +++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/client/CloudBlob.java @@ -671,6 +671,8 @@ public final CloudBlob createSnapshot() throws StorageException { @DoesServiceRequest public final CloudBlob createSnapshot(final AccessCondition accessCondition, BlobRequestOptions options, OperationContext opContext) throws StorageException { + assertNoWriteOperationForSnapshot(); + if (opContext == null) { opContext = new OperationContext(); } @@ -2311,6 +2313,8 @@ public abstract void upload(InputStream sourceStream, long length, final AccessC protected final void uploadFullBlob(final InputStream sourceStream, final long length, final AccessCondition accessCondition, final BlobRequestOptions options, final OperationContext opContext) throws StorageException, IOException { + assertNoWriteOperationForSnapshot(); + // Mark sourceStream for current position. sourceStream.mark(Constants.MAX_MARK_LENGTH); @@ -2394,6 +2398,7 @@ public final void uploadMetadata() throws StorageException { @DoesServiceRequest public final void uploadMetadata(final AccessCondition accessCondition, BlobRequestOptions options, OperationContext opContext) throws StorageException { + assertNoWriteOperationForSnapshot(); if (opContext == null) { opContext = new OperationContext(); } @@ -2465,6 +2470,8 @@ public final void uploadProperties() throws StorageException { @DoesServiceRequest public final void uploadProperties(final AccessCondition accessCondition, BlobRequestOptions options, OperationContext opContext) throws StorageException { + assertNoWriteOperationForSnapshot(); + if (opContext == null) { opContext = new OperationContext(); } @@ -2505,4 +2512,13 @@ public Void execute(final CloudBlobClient client, final CloudBlob blob, final Op ExecutionEngine .executeWithRetry(this.blobServiceClient, this, impl, options.getRetryPolicyFactory(), opContext); } + + /** + * Asserts that write operation is not done for snapshot. + */ + private void assertNoWriteOperationForSnapshot() { + if (isSnapshot()) { + throw new IllegalArgumentException("Cannot perform this operation on a blob representing a snapshot."); + } + } } diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/queue/client/CloudQueue.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/queue/client/CloudQueue.java index f77e7931062e0..d5dd8599eafc1 100644 --- a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/queue/client/CloudQueue.java +++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/queue/client/CloudQueue.java @@ -637,6 +637,9 @@ public void deleteMessage(final CloudQueueMessage message) throws StorageExcepti public void deleteMessage(final CloudQueueMessage message, QueueRequestOptions options, OperationContext opContext) throws StorageException { Utility.assertNotNull("message", message); + Utility.assertNotNullOrEmpty("messageId", message.id); + Utility.assertNotNullOrEmpty("popReceipt", message.popReceipt); + if (opContext == null) { opContext = new OperationContext(); } @@ -1072,7 +1075,7 @@ public CloudQueueMessage retrieveMessage() throws StorageException { @DoesServiceRequest public CloudQueueMessage retrieveMessage(final int visibilityTimeoutInSeconds, final QueueRequestOptions options, final OperationContext opContext) throws StorageException { - return getFirstOrNull(this.retrieveMessages(1, visibilityTimeoutInSeconds, null, null)); + return getFirstOrNull(this.retrieveMessages(1, visibilityTimeoutInSeconds, options, opContext)); } /** diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/AtomPubParser.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/AtomPubParser.java index 4b462cb334137..c258badfc3ff2 100644 --- a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/AtomPubParser.java +++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/AtomPubParser.java @@ -416,8 +416,8 @@ protected static void writeEntityToStream(final TableEntity entity, final boolea } if (!isTableEntry) { - Utility.assertNotNullOrEmpty(TableConstants.PARTITION_KEY, entity.getPartitionKey()); - Utility.assertNotNullOrEmpty(TableConstants.ROW_KEY, entity.getRowKey()); + Utility.assertNotNull(TableConstants.PARTITION_KEY, entity.getPartitionKey()); + Utility.assertNotNull(TableConstants.ROW_KEY, entity.getRowKey()); Utility.assertNotNull(TableConstants.TIMESTAMP, entity.getTimestamp()); } diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/QueryTableOperation.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/QueryTableOperation.java index 74098b013074e..a79824446fc29 100644 --- a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/QueryTableOperation.java +++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/QueryTableOperation.java @@ -62,7 +62,7 @@ protected QueryTableOperation() { */ QueryTableOperation(final String partitionKey, final String rowKey) { super(null, TableOperationType.RETRIEVE); - Utility.assertNotNullOrEmpty("partitionKey", partitionKey); + Utility.assertNotNull("partitionKey", partitionKey); this.partitionKey = partitionKey; this.rowKey = rowKey; } diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/blob/client/CloudBlobContainerTests.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/blob/client/CloudBlobContainerTests.java index c9da0ea84377e..5ce024c11183c 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/blob/client/CloudBlobContainerTests.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/blob/client/CloudBlobContainerTests.java @@ -498,4 +498,82 @@ public void testCopyFromBlobAbortTest() throws StorageException, URISyntaxExcept newContainer.deleteIfExists(); } } + + @Test + public void testBlobSnapshotValidationTest() throws StorageException, URISyntaxException, IOException, + InterruptedException { + String name = generateRandomContainerName(); + CloudBlobContainer newContainer = bClient.getContainerReference(name); + newContainer.create(); + + try { + final int length = 1 * 1024; + final Random randGenerator = new Random(); + final byte[] buff = new byte[length]; + randGenerator.nextBytes(buff); + + String blockBlobName = "testBlockBlob" + Integer.toString(randGenerator.nextInt(50000)); + blockBlobName = blockBlobName.replace('-', '_'); + + final CloudBlob blockBlobRef = newContainer.getBlockBlobReference(blockBlobName); + blockBlobRef.upload(new ByteArrayInputStream(buff), -1, null, null, null); + + final CloudBlob blobSnapshot = blockBlobRef.createSnapshot(); + + final ByteArrayOutputStream outStream = new ByteArrayOutputStream(length); + + blobSnapshot.download(outStream); + final byte[] retrievedBuff = outStream.toByteArray(); + for (int m = 0; m < length; m++) { + Assert.assertEquals(buff[m], retrievedBuff[m]); + } + + // Read operation should work fine. + blobSnapshot.downloadAttributes(); + + // Expect an IllegalArgumentException from upload. + try { + blobSnapshot.upload(new ByteArrayInputStream(buff), -1); + Assert.fail("Expect an IllegalArgumentException from upload"); + } + catch (IllegalArgumentException e) { + Assert.assertEquals("Cannot perform this operation on a blob representing a snapshot.", e.getMessage()); + } + + // Expect an IllegalArgumentException from uploadMetadata. + try { + blobSnapshot.uploadMetadata(); + Assert.fail("Expect an IllegalArgumentException from uploadMetadata"); + } + catch (IllegalArgumentException e) { + Assert.assertEquals("Cannot perform this operation on a blob representing a snapshot.", e.getMessage()); + } + + // Expect an IllegalArgumentException from uploadProperties. + try { + blobSnapshot.uploadProperties(); + Assert.fail("Expect an IllegalArgumentException from uploadProperties"); + } + catch (IllegalArgumentException e) { + Assert.assertEquals("Cannot perform this operation on a blob representing a snapshot.", e.getMessage()); + } + + // Expect an IllegalArgumentException from createSnapshot. + try { + blobSnapshot.createSnapshot(); + Assert.fail("Expect an IllegalArgumentException from createSnapshot"); + } + catch (IllegalArgumentException e) { + Assert.assertEquals("Cannot perform this operation on a blob representing a snapshot.", e.getMessage()); + } + + blobSnapshot.delete(DeleteSnapshotsOption.NONE, null, null, null); + + blockBlobRef.downloadAttributes(); + } + finally { + // cleanup + newContainer.deleteIfExists(); + } + } } diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/queue/client/CloudQueueTests.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/queue/client/CloudQueueTests.java index a8ec75d631c01..182a004c6071a 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/queue/client/CloudQueueTests.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/queue/client/CloudQueueTests.java @@ -19,6 +19,7 @@ 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; @@ -537,6 +538,77 @@ public void testAddMessage() throws URISyntaxException, StorageException, Unsupp queue.delete(); } + @Test + public void testAddMessageUnicode() throws URISyntaxException, StorageException, UnsupportedEncodingException { + final String queueName = UUID.randomUUID().toString().toLowerCase(); + final CloudQueue queue = qClient.getQueueReference(queueName); + queue.create(); + + ArrayList messages = new ArrayList(); + messages.add("Le débat sur l'identité nationale, l'idée du président Nicolas Sarkozy de déchoir des personnes d'origine étrangère de la nationalité française dans certains cas et les récentes mesures prises contre les Roms ont choqué les experts, qui rendront leurs conclusions le 27 août."); + messages.add("Ваш логин Yahoo! дает доступ к таким мощным инструментам связи, как электронная почта, отправка мгновенных сообщений, функции безопасности, в частности, антивирусные средства и блокировщик всплывающей рекламы, и избранное, например, фото и музыка в сети — все бесплат"); + messages.add("据新华社8月12日电 8月11日晚,舟曲境内再次出现强降雨天气,使特大山洪泥石流灾情雪上加霜。白龙江水在梨坝子村的交汇地带形成一个新的堰塞湖,水位比平时高出3米。甘肃省国土资源厅副厅长张国华当日22时许在新闻发布会上介绍,截至12日21时50分,舟曲堰塞湖堰塞体已消除,溃坝险情已消除,目前针对堰塞湖的主要工作是疏通河道。"); + messages.add("ל כולם\", הדהים יעלון, ויישר קו עם העדות שמסר ראש הממשלה, בנימין נתניהו, לוועדת טירקל. לדבריו, אכן השרים דנו רק בהיבטים התקשורתיים של עצירת המשט: \"בשביעייה לא התקיים דיון על האלטרנטיבות. עסקנו בהיבטים "); + messages.add("Prozent auf 0,5 Prozent. Im Vergleich zum Vorjahresquartal wuchs die deutsche Wirtschaft von Januar bis März um 2,1 Prozent. Auch das ist eine Korrektur nach oben, ursprünglich waren es hier 1,7 Prozent"); + messages.add("\n\n\n\n Computer Parts\n \n Motherboard\n ASUS\n " + + "P3B-F\n 123.00\n \n \n Video Card\n ATI\n All-in-Wonder Pro\n 160.00\n \n \n Sound Card\n " + + "Creative Labs\n Sound Blaster Live\n 80.00\n \n \n inch Monitor\n LG Electronics\n 995E\n 290.00\n \n"); + + for (int i = 0; i < messages.size(); i++) { + String msg = messages.get(i); + queue.addMessage(new CloudQueueMessage(msg)); + CloudQueueMessage readBack = queue.retrieveMessage(); + Assert.assertEquals(msg, readBack.getMessageContentAsString()); + queue.deleteMessage(readBack); + } + + queue.shouldEncodeMessage = false; + for (int i = 0; i < messages.size(); i++) { + String msg = messages.get(i); + queue.addMessage(new CloudQueueMessage(msg)); + CloudQueueMessage readBack = queue.retrieveMessage(); + Assert.assertEquals(msg, readBack.getMessageContentAsString()); + queue.deleteMessage(readBack); + } + + queue.delete(); + } + + @Test + public void testAddMessageLargeVisibilityDelay() throws URISyntaxException, StorageException, + UnsupportedEncodingException { + final String queueName = UUID.randomUUID().toString().toLowerCase(); + final CloudQueue queue = qClient.getQueueReference(queueName); + queue.create(); + + String msgContent = UUID.randomUUID().toString(); + final CloudQueueMessage message = new CloudQueueMessage(msgContent); + queue.addMessage(message, 10, 20, null, null); + CloudQueueMessage msgFromRetrieve1 = queue.retrieveMessage(); + Assert.assertEquals(message.getMessageContentAsString(), msgContent); + Assert.assertEquals(msgFromRetrieve1.getMessageContentAsString(), msgContent); + + queue.delete(); + } + + @Test + public void testDeleteMessageWithDifferentQueueInstance() throws URISyntaxException, StorageException, + UnsupportedEncodingException { + final String queueName = UUID.randomUUID().toString().toLowerCase(); + final CloudQueue queue1 = qClient.getQueueReference(queueName); + queue1.create(); + + String msgContent = UUID.randomUUID().toString(); + final CloudQueueMessage message = new CloudQueueMessage(msgContent); + queue1.addMessage(message); + CloudQueueMessage msgFromRetrieved = queue1.retrieveMessage(); + + final CloudQueue queue2 = qClient.getQueueReference(queueName); + queue2.deleteMessage(msgFromRetrieved); + + queue1.delete(); + } + @Test public void testAddMessageToNonExistingQueue() throws URISyntaxException, StorageException, UnsupportedEncodingException { @@ -752,10 +824,14 @@ public void testRetrieveMessage() throws URISyntaxException, StorageException, U CloudQueue newQueue = qClient.getQueueReference(queueName); newQueue.create(); newQueue.addMessage(new CloudQueueMessage("message"), 20, 0, null, null); - CloudQueueMessage message1 = newQueue.retrieveMessage(); + OperationContext opContext = new OperationContext(); + CloudQueueMessage message1 = newQueue.retrieveMessage(10, null /*QueueRequestOptions*/, opContext); Date expirationTime1 = message1.getExpirationTime(); Date insertionTime1 = message1.getInsertionTime(); Date nextVisibleTime1 = message1.getNextVisibleTime(); + + Assert.assertEquals(HttpURLConnection.HTTP_OK, opContext.getLastResult().getStatusCode()); + newQueue.deleteMessage(message1); try { diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/table/client/TableClientTests.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/table/client/TableClientTests.java index 86c5056cf9f73..ec05e10cee028 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/table/client/TableClientTests.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/table/client/TableClientTests.java @@ -507,8 +507,8 @@ public void testTableSASFromPermission() throws StorageException, URISyntaxExcep now.add(Calendar.MINUTE, 10); policy1.setSharedAccessExpiryTime(now.getTime()); - policy1.setPermissions(EnumSet.of(SharedAccessTablePermissions.ADD, SharedAccessTablePermissions.QUERY, - SharedAccessTablePermissions.UPDATE, SharedAccessTablePermissions.DELETE)); + policy1.setPermissions(EnumSet.of(SharedAccessTablePermissions.QUERY, SharedAccessTablePermissions.UPDATE, + SharedAccessTablePermissions.DELETE)); expectedPermissions.getSharedAccessPolicies().put(identifier, policy1); table.uploadPermissions(expectedPermissions); @@ -532,6 +532,16 @@ public void testTableSASFromPermission() throws StorageException, URISyntaxExcep "javatables_batch_0", null, "javatables_batch_9", null); { + TableBatchOperation batchFromSAS = new TableBatchOperation(); + + for (int j = 1000; j < 1010; j++) { + class1 ent = generateRandomEnitity("javatables_batch_" + Integer.toString(0)); + ent.setRowKey(String.format("%06d", j)); + batchFromSAS.insert(ent); + } + + tableClientFromPermission.execute(name, batchFromSAS); + class1 randEnt = TableTestBase.generateRandomEnitity(null); TableQuery query = TableQuery.from(name, class1.class).where( String.format("(PartitionKey eq '%s') and (RowKey ge '%s')", "javatables_batch_1", "000050")); @@ -581,10 +591,11 @@ public void testTableSASFromPermission() throws StorageException, URISyntaxExcep secondEntity.setRowKey(baseEntity.getRowKey()); secondEntity.setEtag(baseEntity.getEtag()); - // Insert or merge Entity - ENTITY DOES NOT EXIST NOW. TableResult insertResult = tableClientFromPermission.execute(name, TableOperation.insertOrMerge(baseEntity)); + // Insert or merge Entity - ENTITY DOES NOT EXIST NOW. + Assert.assertEquals(insertResult.getHttpStatusCode(), HttpURLConnection.HTTP_NO_CONTENT); // Insert or replace Entity - ENTITY EXISTS -> WILL REPLACE diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/table/client/TableSerializerTests.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/table/client/TableSerializerTests.java index e7380c67c6fe6..e3d19000d7481 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/table/client/TableSerializerTests.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/table/client/TableSerializerTests.java @@ -248,6 +248,25 @@ public void whitespaceTest() throws StorageException { Assert.assertEquals(((class1) res.getResult()).getA(), ref.getA()); } + @Test + public void whitespaceOnEmptyKeysTest() throws StorageException { + class1 ref = new class1(); + + ref.setA("B "); + ref.setB(" A "); + ref.setC(" "); + ref.setD(new byte[] { 0, 1, 2 }); + ref.setPartitionKey(""); + ref.setRowKey(""); + + tClient.execute(testSuiteTableName, TableOperation.insert(ref)); + + TableResult res = tClient.execute(testSuiteTableName, + TableOperation.retrieve(ref.getPartitionKey(), ref.getRowKey(), class1.class)); + + Assert.assertEquals(((class1) res.getResult()).getA(), ref.getA()); + } + @Test public void newLineTest() throws StorageException { class1 ref = new class1(); From 5b62c0039a5fca08678b2297488bf89082941d7f Mon Sep 17 00:00:00 2001 From: "Justin Yu (Microsoft)" Date: Tue, 21 Aug 2012 16:13:22 -0700 Subject: [PATCH 04/17] "Java bug fixes" --- .../windowsazure/services/blob/client/CloudBlob.java | 3 ++- .../windowsazure/services/blob/client/CloudBlockBlob.java | 6 ++++++ .../windowsazure/services/blob/client/CloudPageBlob.java | 6 ++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/client/CloudBlob.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/client/CloudBlob.java index 63043cae64301..d686c88ab8d98 100644 --- a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/client/CloudBlob.java +++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/client/CloudBlob.java @@ -2399,6 +2399,7 @@ public final void uploadMetadata() throws StorageException { public final void uploadMetadata(final AccessCondition accessCondition, BlobRequestOptions options, OperationContext opContext) throws StorageException { assertNoWriteOperationForSnapshot(); + if (opContext == null) { opContext = new OperationContext(); } @@ -2516,7 +2517,7 @@ public Void execute(final CloudBlobClient client, final CloudBlob blob, final Op /** * Asserts that write operation is not done for snapshot. */ - private void assertNoWriteOperationForSnapshot() { + protected void assertNoWriteOperationForSnapshot() { if (isSnapshot()) { throw new IllegalArgumentException("Cannot perform this operation on a blob representing a snapshot."); } diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/client/CloudBlockBlob.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/client/CloudBlockBlob.java index c36d4993bf27c..2266499597f40 100644 --- a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/client/CloudBlockBlob.java +++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/client/CloudBlockBlob.java @@ -162,6 +162,8 @@ public void commitBlockList(final Iterable blockList) throws Storage @DoesServiceRequest public void commitBlockList(final Iterable blockList, final AccessCondition accessCondition, BlobRequestOptions options, OperationContext opContext) throws StorageException { + assertNoWriteOperationForSnapshot(); + if (opContext == null) { opContext = new OperationContext(); } @@ -406,6 +408,8 @@ public void upload(final InputStream sourceStream, final long length, final Acce "Invalid stream length, specify -1 for unkown length stream, or a positive number of bytes"); } + assertNoWriteOperationForSnapshot(); + if (opContext == null) { opContext = new OperationContext(); } @@ -520,6 +524,8 @@ public void uploadBlock(final String blockId, final InputStream sourceStream, fi "Invalid stream length, length must be less than or equal to 4 MB in size."); } + assertNoWriteOperationForSnapshot(); + if (opContext == null) { opContext = new OperationContext(); } diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/client/CloudPageBlob.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/client/CloudPageBlob.java index d55d8564b0b6d..f6829b8339515 100644 --- a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/client/CloudPageBlob.java +++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/client/CloudPageBlob.java @@ -240,6 +240,8 @@ public void create(final long length) throws StorageException { @DoesServiceRequest public void create(final long length, final AccessCondition accessCondition, BlobRequestOptions options, OperationContext opContext) throws StorageException { + assertNoWriteOperationForSnapshot(); + if (length % BlobConstants.PAGE_SIZE != 0) { throw new IllegalArgumentException("Page blob length must be multiple of 512."); } @@ -541,6 +543,8 @@ public void upload(final InputStream sourceStream, final long length) throws Sto @DoesServiceRequest public void upload(final InputStream sourceStream, final long length, final AccessCondition accessCondition, BlobRequestOptions options, OperationContext opContext) throws StorageException, IOException { + assertNoWriteOperationForSnapshot(); + if (opContext == null) { opContext = new OperationContext(); } @@ -644,6 +648,8 @@ public void uploadPages(final InputStream sourceStream, final long offset, final throw new IllegalArgumentException("Max write size is 4MB. Please specify a smaller range."); } + assertNoWriteOperationForSnapshot(); + if (opContext == null) { opContext = new OperationContext(); } From 88bac0555e535fa01e8257d0b77ee0fae1089acc Mon Sep 17 00:00:00 2001 From: "Justin Yu (Microsoft)" Date: Tue, 21 Aug 2012 16:56:29 -0700 Subject: [PATCH 05/17] Add more validation for Snapshot --- .../microsoft/windowsazure/services/blob/client/CloudBlob.java | 2 ++ .../windowsazure/services/blob/client/CloudBlockBlob.java | 2 ++ .../windowsazure/services/blob/client/CloudPageBlob.java | 2 ++ 3 files changed, 6 insertions(+) diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/client/CloudBlob.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/client/CloudBlob.java index d686c88ab8d98..e0cbeb4cd7f9d 100644 --- a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/client/CloudBlob.java +++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/client/CloudBlob.java @@ -1743,6 +1743,8 @@ public final BlobInputStream openInputStream(final AccessCondition accessConditi options = new BlobRequestOptions(); } + assertNoWriteOperationForSnapshot(); + options.applyDefaults(this.blobServiceClient); return new BlobInputStream(this, accessCondition, options, opContext); diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/client/CloudBlockBlob.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/client/CloudBlockBlob.java index 2266499597f40..8560f989a5d20 100644 --- a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/client/CloudBlockBlob.java +++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/client/CloudBlockBlob.java @@ -352,6 +352,8 @@ public BlobOutputStream openOutputStream(final AccessCondition accessCondition, options = new BlobRequestOptions(); } + assertNoWriteOperationForSnapshot(); + options.applyDefaults(this.blobServiceClient); return new BlobOutputStream(this, accessCondition, options, opContext); diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/client/CloudPageBlob.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/client/CloudPageBlob.java index f6829b8339515..7e8256487b050 100644 --- a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/client/CloudPageBlob.java +++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/client/CloudPageBlob.java @@ -418,6 +418,8 @@ public BlobOutputStream openOutputStream(final long length, final AccessConditio options = new BlobRequestOptions(); } + assertNoWriteOperationForSnapshot(); + options.applyDefaults(this.blobServiceClient); if (options.getStoreBlobContentMD5()) { From 99264c0794b56a9b2956ab39da1cfcb13677abdb Mon Sep 17 00:00:00 2001 From: "Justin Yu (Microsoft)" Date: Wed, 26 Sep 2012 12:16:34 -0700 Subject: [PATCH 06/17] Update read response header process in all the blob requests. --- .../services/blob/client/CloudBlob.java | 58 +++++++++++-------- .../services/blob/client/CloudBlockBlob.java | 8 ++- .../services/blob/client/CloudPageBlob.java | 7 ++- 3 files changed, 44 insertions(+), 29 deletions(-) diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/client/CloudBlob.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/client/CloudBlob.java index e0cbeb4cd7f9d..54219325c8895 100644 --- a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/client/CloudBlob.java +++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/client/CloudBlob.java @@ -300,7 +300,6 @@ public String execute(final CloudBlobClient client, final CloudBlob blob, final return null; } - blob.updatePropertiesFromResponse(request); blob.properties.setLeaseStatus(LeaseStatus.LOCKED); return BlobResponse.getLeaseID(request, opContext); @@ -413,7 +412,7 @@ public Long execute(final CloudBlobClient client, final CloudBlob blob, final Op return -1L; } - blob.updatePropertiesFromResponse(request); + blob.updateEtagAndLastModifiedFromResponse(request); final String leaseTime = BlobResponse.getLeaseTime(request, opContext); blob.properties.setLeaseStatus(LeaseStatus.UNLOCKED); @@ -547,7 +546,7 @@ public Void execute(final CloudBlobClient client, final CloudBlob blob, final Op return null; } - blob.updatePropertiesFromResponse(request); + blob.updateEtagAndLastModifiedFromResponse(request); blob.copyState = BaseResponse.getCopyState(request); return null; @@ -626,8 +625,6 @@ public Void execute(final CloudBlobClient client, final CloudBlob blob, final Op return null; } - blob.updatePropertiesFromResponse(request); - return null; } }; @@ -711,7 +708,7 @@ else if (blob instanceof CloudPageBlob) { snapshot = new CloudPageBlob(blob.getUri(), snapshotTime, client); } - blob.updatePropertiesFromResponse(request); + blob.updateEtagAndLastModifiedFromResponse(request); return snapshot; } }; @@ -878,7 +875,6 @@ public Boolean execute(final CloudBlobClient client, final CloudBlob blob, final this.setResult(ExecutionEngine.processRequest(request, opContext)); - blob.updatePropertiesFromResponse(request); if (this.getResult().getStatusCode() == HttpURLConnection.HTTP_ACCEPTED) { return true; } @@ -970,7 +966,11 @@ public Void execute(final CloudBlobClient client, final CloudBlob blob, final Op final String contentLength = request.getHeaderField(Constants.HeaderConstants.CONTENT_LENGTH); final long expectedLength = Long.parseLong(contentLength); - blob.updatePropertiesFromResponse(request); + final BlobAttributes retrievedAttributes = BlobResponse.getAttributes(request, blob.getUri(), + blob.snapshotID, opContext); + blob.properties = retrievedAttributes.getProperties(); + blob.metadata = retrievedAttributes.getMetadata(); + blob.copyState = retrievedAttributes.getCopyState(); ExecutionEngine.getResponseCode(this.getResult(), request, opContext); if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_OK) { @@ -1323,7 +1323,15 @@ public Void execute(final CloudBlobClient client, final CloudBlob blob, final Op return null; } - blob.updatePropertiesFromResponse(request); + // Do not update blob length in downloadRangeInternal API. + final long orignalBlobLength = blob.properties.getLength(); + final BlobAttributes retrievedAttributes = BlobResponse.getAttributes(request, blob.getUri(), + blob.snapshotID, opContext); + blob.properties = retrievedAttributes.getProperties(); + blob.metadata = retrievedAttributes.getMetadata(); + blob.copyState = retrievedAttributes.getCopyState(); + blob.properties.setLength(orignalBlobLength); + final String contentLength = request.getHeaderField(Constants.HeaderConstants.CONTENT_LENGTH); final long expectedLength = Long.parseLong(contentLength); if (totalRead != expectedLength) { @@ -1423,7 +1431,11 @@ public Boolean execute(final CloudBlobClient client, final CloudBlob blob, final this.setResult(ExecutionEngine.processRequest(request, opContext)); if (this.getResult().getStatusCode() == HttpURLConnection.HTTP_OK) { - blob.updatePropertiesFromResponse(request); + final BlobAttributes retrievedAttributes = BlobResponse.getAttributes(request, blob.getUri(), + blob.snapshotID, opContext); + blob.properties = retrievedAttributes.getProperties(); + blob.metadata = retrievedAttributes.getMetadata(); + blob.copyState = retrievedAttributes.getCopyState(); return Boolean.valueOf(true); } else if (this.getResult().getStatusCode() == HttpURLConnection.HTTP_NOT_FOUND) { @@ -1819,7 +1831,7 @@ protected void parseURIQueryStringAndVerify(final URI completeUri, final CloudBl } } - void updatePropertiesFromResponse(HttpURLConnection request) { + void updateEtagAndLastModifiedFromResponse(HttpURLConnection request) { String tempStr = request.getHeaderField(Constants.HeaderConstants.ETAG); // ETag @@ -1835,12 +1847,12 @@ void updatePropertiesFromResponse(HttpURLConnection request) { this.getProperties().setLastModified(lastModifiedCalendar.getTime()); } - // using this instead of the request property since the request - // property only returns an int. - tempStr = request.getHeaderField(Constants.HeaderConstants.CONTENT_LENGTH); + } - if (!Utility.isNullOrEmpty(tempStr)) { - this.getProperties().setLength(Long.parseLong(tempStr)); + void updateLengthFromResponse(HttpURLConnection request) { + final String xContentLengthHeader = request.getHeaderField(BlobConstants.CONTENT_LENGTH_HEADER); + if (!Utility.isNullOrEmpty(xContentLengthHeader)) { + this.getProperties().setLength(Long.parseLong(xContentLengthHeader)); } } @@ -1914,7 +1926,7 @@ public Void execute(final CloudBlobClient client, final CloudBlob blob, final Op return null; } - blob.updatePropertiesFromResponse(request); + blob.updateEtagAndLastModifiedFromResponse(request); blob.properties.setLeaseStatus(LeaseStatus.UNLOCKED); return null; } @@ -1994,7 +2006,7 @@ public Void execute(final CloudBlobClient client, final CloudBlob blob, final Op return null; } - blob.updatePropertiesFromResponse(request); + blob.updateEtagAndLastModifiedFromResponse(request); return null; } }; @@ -2082,7 +2094,7 @@ public Void execute(final CloudBlobClient client, final CloudBlob blob, final Op return null; } - blob.updatePropertiesFromResponse(request); + blob.updateEtagAndLastModifiedFromResponse(request); return null; } }; @@ -2229,7 +2241,7 @@ public Long execute(final CloudBlobClient client, final CloudBlob blob, final Op return -1L; } - blob.updatePropertiesFromResponse(request); + blob.updateEtagAndLastModifiedFromResponse(request); final String leaseTime = BlobResponse.getLeaseTime(request, opContext); return Utility.isNullOrEmpty(leaseTime) ? -1L : Long.parseLong(leaseTime); @@ -2359,7 +2371,7 @@ public Void execute(final CloudBlobClient client, final CloudBlob blob, final Op return null; } - blob.updatePropertiesFromResponse(request); + blob.updateEtagAndLastModifiedFromResponse(request); return null; } }; @@ -2433,7 +2445,7 @@ public Void execute(final CloudBlobClient client, final CloudBlob blob, final Op return null; } - blob.updatePropertiesFromResponse(request); + blob.updateEtagAndLastModifiedFromResponse(request); return null; } }; @@ -2507,7 +2519,7 @@ public Void execute(final CloudBlobClient client, final CloudBlob blob, final Op return null; } - blob.updatePropertiesFromResponse(request); + blob.updateEtagAndLastModifiedFromResponse(request); return null; } }; diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/client/CloudBlockBlob.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/client/CloudBlockBlob.java index 8560f989a5d20..350cf65c52674 100644 --- a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/client/CloudBlockBlob.java +++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/client/CloudBlockBlob.java @@ -211,7 +211,7 @@ public Void execute(final CloudBlobClient client, final CloudBlob blob, final Op return null; } - blob.updatePropertiesFromResponse(request); + blob.updateEtagAndLastModifiedFromResponse(request); return null; } }; @@ -300,7 +300,9 @@ public ArrayList execute(final CloudBlobClient client, final CloudBl return null; } - blob.updatePropertiesFromResponse(request); + blob.updateEtagAndLastModifiedFromResponse(request); + blob.updateLengthFromResponse(request); + final GetBlockListResponse response = new GetBlockListResponse(request.getInputStream()); return response.getBlocks(); } @@ -629,7 +631,7 @@ public Void execute(final CloudBlobClient client, final CloudBlob blob, final Op return null; } - blob.updatePropertiesFromResponse(request); + blob.updateEtagAndLastModifiedFromResponse(request); return null; } }; diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/client/CloudPageBlob.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/client/CloudPageBlob.java index 7e8256487b050..f5ce8f06f4c8b 100644 --- a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/client/CloudPageBlob.java +++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/client/CloudPageBlob.java @@ -279,7 +279,7 @@ public Void execute(final CloudBlobClient client, final CloudBlob blob, final Op return null; } - blob.updatePropertiesFromResponse(request); + blob.updateEtagAndLastModifiedFromResponse(request); return null; } }; @@ -359,7 +359,7 @@ public ArrayList execute(final CloudBlobClient client, final CloudBlo return null; } - blob.updatePropertiesFromResponse(request); + blob.updateEtagAndLastModifiedFromResponse(request); final GetPageRangesResponse response = new GetPageRangesResponse(request.getInputStream()); return response.getPageRanges(); } @@ -489,7 +489,8 @@ public Void execute(final CloudBlobClient client, final CloudBlob blob, final Op return null; } - blob.updatePropertiesFromResponse(request); + blob.updateEtagAndLastModifiedFromResponse(request); + blob.updateLengthFromResponse(request); return null; } }; From b47a9de92ff782d938e5d013816dd84287be656d Mon Sep 17 00:00:00 2001 From: "Justin Yu (Microsoft)" Date: Wed, 26 Sep 2012 13:50:20 -0700 Subject: [PATCH 07/17] Add tests for download range validation. --- .../blob/client/CloudBlobContainerTests.java | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/blob/client/CloudBlobContainerTests.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/blob/client/CloudBlobContainerTests.java index 5ce024c11183c..9847dea2b26af 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/blob/client/CloudBlobContainerTests.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/blob/client/CloudBlobContainerTests.java @@ -21,6 +21,7 @@ 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; @@ -576,4 +577,54 @@ public void testBlobSnapshotValidationTest() throws StorageException, URISyntaxE newContainer.deleteIfExists(); } } + + @Test + public void testBlobDownloadRangeValidationTest() throws StorageException, URISyntaxException, IOException, + InterruptedException { + String name = generateRandomContainerName(); + CloudBlobContainer newContainer = bClient.getContainerReference(name); + newContainer.create(); + + try { + + final int blockLength = 1024 * 1024; + final int length = 3 * blockLength; + final Random randGenerator = new Random(); + final byte[] buff = new byte[length]; + randGenerator.nextBytes(buff); + + String blockBlobName = "testBlockBlob" + Integer.toString(randGenerator.nextInt(50000)); + blockBlobName = blockBlobName.replace('-', '_'); + + final CloudBlockBlob blockBlobRef = newContainer.getBlockBlobReference(blockBlobName); + blockBlobRef.upload(new ByteArrayInputStream(buff), -1, null, null, null); + ArrayList blockList = new ArrayList(); + for (int i = 1; i <= 3; i++) { + randGenerator.nextBytes(buff); + + String blockID = String.format("%08d", i); + blockBlobRef.uploadBlock(blockID, new ByteArrayInputStream(buff), blockLength, null, null, null); + blockList.add(new BlockEntry(blockID, BlockSearchMode.LATEST)); + } + + blockBlobRef.commitBlockList(blockList); + + //Download full blob + blockBlobRef.download(new ByteArrayOutputStream()); + Assert.assertEquals(length, blockBlobRef.getProperties().getLength()); + + //Download blob range. + byte[] downloadBuffer = new byte[100]; + blockBlobRef.downloadRange(0, 100, downloadBuffer, 0); + Assert.assertEquals(length, blockBlobRef.getProperties().getLength()); + + //Download block list. + blockBlobRef.downloadBlockList(); + Assert.assertEquals(length, blockBlobRef.getProperties().getLength()); + } + finally { + // cleanup + newContainer.deleteIfExists(); + } + } } From f1cbf9c10e6b58004af69645c9bcc61ed362894e Mon Sep 17 00:00:00 2001 From: "Justin Yu (Microsoft)" Date: Wed, 3 Oct 2012 14:20:48 -0700 Subject: [PATCH 08/17] Bug fix for listContainer. Add the test to verify + in blob name. --- .../client/BlobDeserializationHelper.java | 2 +- .../blob/client/CloudBlobContainerTests.java | 44 +++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/client/BlobDeserializationHelper.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/client/BlobDeserializationHelper.java index 10fda315c1fb0..785eed98292d3 100644 --- a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/client/BlobDeserializationHelper.java +++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/blob/client/BlobDeserializationHelper.java @@ -317,7 +317,7 @@ else if (name.equals(Constants.ETAG_ELEMENT)) { } else if (name.equals(Constants.LEASE_STATUS_ELEMENT)) { properties.setLeaseStatus(LeaseStatus.parse(Utility.readElementFromXMLReader(xmlr, - Constants.COPY_STATUS_ELEMENT))); + Constants.LEASE_STATUS_ELEMENT))); } else if (name.equals(Constants.LEASE_STATE_ELEMENT)) { properties.setLeaseState(LeaseState.parse(Utility.readElementFromXMLReader(xmlr, diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/blob/client/CloudBlobContainerTests.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/blob/client/CloudBlobContainerTests.java index 9847dea2b26af..d7602c93d75bc 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/blob/client/CloudBlobContainerTests.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/blob/client/CloudBlobContainerTests.java @@ -37,6 +37,7 @@ import com.microsoft.windowsazure.services.core.storage.AccessCondition; import com.microsoft.windowsazure.services.core.storage.OperationContext; +import com.microsoft.windowsazure.services.core.storage.ResultSegment; import com.microsoft.windowsazure.services.core.storage.StorageCredentialsSharedAccessSignature; import com.microsoft.windowsazure.services.core.storage.StorageErrorCodeStrings; import com.microsoft.windowsazure.services.core.storage.StorageException; @@ -627,4 +628,47 @@ public void testBlobDownloadRangeValidationTest() throws StorageException, URISy newContainer.deleteIfExists(); } } + + @Test + public void testBlobNamePlusEncodingTest() throws StorageException, URISyntaxException, IOException, + InterruptedException { + String name = generateRandomContainerName(); + CloudBlobContainer newContainer = bClient.getContainerReference(name); + newContainer.create(); + + try { + + final int length = 1 * 1024; + final Random randGenerator = new Random(); + final byte[] buff = new byte[length]; + randGenerator.nextBytes(buff); + + final CloudBlockBlob originalBlob = newContainer.getBlockBlobReference("a+b.txt"); + originalBlob.upload(new ByteArrayInputStream(buff), -1, null, null, null); + + CloudBlob copyBlob = newContainer.getBlockBlobReference(originalBlob.getName() + "copyed"); + copyBlob.copyFromBlob(originalBlob); + Thread.sleep(1000); + copyBlob.downloadAttributes(); + } + finally { + // cleanup + newContainer.deleteIfExists(); + } + } + + @Test + public void testListContainersTest() throws StorageException, URISyntaxException, IOException, InterruptedException { + final ResultSegment segment = bClient.listContainersSegmented(null, + ContainerListingDetails.ALL, 2, null, null, null); + + for (int i = 0; i < 5 && segment.getHasMoreResults(); i++) { + for (final CloudBlobContainer container : segment.getResults()) { + container.downloadAttributes(); + } + + bClient.listContainersSegmented(null, ContainerListingDetails.ALL, 2, segment.getContinuationToken(), null, + null); + } + } } From 33c432b03a3b5d8751500030b75cdac8312368bc Mon Sep 17 00:00:00 2001 From: "Justin Yu (Microsoft)" Date: Wed, 3 Oct 2012 14:48:02 -0700 Subject: [PATCH 09/17] Remove unnessary imports. --- .../services/queue/client/QueuePermissions.java | 6 ++---- .../services/queue/client/SharedAccessQueuePolicy.java | 7 +++---- .../windowsazure/services/table/client/CloudTable.java | 7 +++---- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/queue/client/QueuePermissions.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/queue/client/QueuePermissions.java index c502533633f14..35649a0fdf364 100644 --- a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/queue/client/QueuePermissions.java +++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/queue/client/QueuePermissions.java @@ -17,8 +17,6 @@ import java.util.HashMap; -import com.microsoft.windowsazure.services.table.client.SharedAccessTablePolicy; - /** * Represents the permissions for a container. */ @@ -51,8 +49,8 @@ public HashMap getSharedAccessPolicies() { * Sets the set of shared access policies for the queue. * * @param sharedAccessPolicies - * The set of shared access policies to set for the queue, represented by a HashMap object of - * {@link SharedAccessQueuePolicy} objects. + * The set of shared access policies to set for the queue, represented by a HashMap object + * of {@link SharedAccessQueuePolicy} objects. */ public void setSharedAccessPolicies(final HashMap sharedAccessPolicies) { this.sharedAccessPolicies = sharedAccessPolicies; diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/queue/client/SharedAccessQueuePolicy.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/queue/client/SharedAccessQueuePolicy.java index 033e8d5056456..6e952ae00a424 100644 --- a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/queue/client/SharedAccessQueuePolicy.java +++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/queue/client/SharedAccessQueuePolicy.java @@ -18,7 +18,6 @@ import java.util.EnumSet; import com.microsoft.windowsazure.services.core.storage.Constants; -import com.microsoft.windowsazure.services.table.client.SharedAccessTablePermissions; /** * Represents a shared access policy, which specifies the start time, expiry time, and permissions for a shared access @@ -31,8 +30,8 @@ public final class SharedAccessQueuePolicy { * * @param value * A String that represents the shared access permissions. The string must contain one or - * more of the following values. Note that they must be lower case, and the order that they are specified must - * be in the order of "rwdl". + * more of the following values. Note that they must be lower case, and the order that they are specified + * must be in the order of "rwdl". *
    *
  • d: Delete access.
  • *
  • l: List access.
  • @@ -159,7 +158,7 @@ public Date getSharedAccessStartTime() { * Sets the permissions for a shared access signature associated with this shared access policy. * * @param permissions - * The permissions, represented by a java.util.EnumSet object that contains + * The permissions, represented by a java.util.EnumSet object that contains * {@link SharedAccessQueuePermissions} values, to set for the shared access signature. */ public void setPermissions(final EnumSet permissions) { diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/CloudTable.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/CloudTable.java index 81f75835ae737..6fe819975c215 100644 --- a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/CloudTable.java +++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/table/client/CloudTable.java @@ -21,7 +21,6 @@ import java.net.URISyntaxException; import java.security.InvalidKeyException; -import com.microsoft.windowsazure.services.blob.client.BlobContainerPermissions; import com.microsoft.windowsazure.services.blob.core.storage.SharedAccessSignatureHelper; import com.microsoft.windowsazure.services.core.storage.DoesServiceRequest; import com.microsoft.windowsazure.services.core.storage.OperationContext; @@ -594,11 +593,11 @@ public TablePermissions execute(final CloudTableClient client, final CloudTable * A table-level access policy. * @return A String containing the shared access signature for the table. * @throws InvalidKeyException - * If an invalid key was passed. + * If an invalid key was passed. * @throws StorageException - * If a storage service error occurred. + * If a storage service error occurred. * @throws IllegalArgumentException - * If an unexpected value is passed. + * If an unexpected value is passed. */ public String generateSharedAccessSignature(final SharedAccessTablePolicy policy, final String accessPolicyIdentifier, final String startPartitionKey, final String startRowKey, From 029ea6d40a6a1f9214333b956f77f65422b7500d Mon Sep 17 00:00:00 2001 From: "Justin Yu (Microsoft)" Date: Fri, 5 Oct 2012 18:00:18 -0700 Subject: [PATCH 10/17] Update the user-agent from 0.1.2 to 0.1.3 for this release. --- .../microsoft/windowsazure/services/core/storage/Constants.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/core/storage/Constants.java b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/core/storage/Constants.java index ae1b15bf0b3de..58e4d2bc8beca 100644 --- a/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/core/storage/Constants.java +++ b/microsoft-azure-api/src/main/java/com/microsoft/windowsazure/services/core/storage/Constants.java @@ -327,7 +327,7 @@ public static class HeaderConstants { /** * Specifies the value to use for UserAgent header. */ - public static final String USER_AGENT_VERSION = "Client v0.1.2"; + public static final String USER_AGENT_VERSION = "Client v0.1.3"; } /** From 384a3877731ed3c2ab8148293a2a53ab2f0f01d1 Mon Sep 17 00:00:00 2001 From: "Justin Yu (Microsoft)" Date: Thu, 18 Oct 2012 13:14:50 -0700 Subject: [PATCH 11/17] Update AddMessageTest --- .../windowsazure/services/queue/client/CloudQueueTests.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/queue/client/CloudQueueTests.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/queue/client/CloudQueueTests.java index 182a004c6071a..f198d34c564d8 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/queue/client/CloudQueueTests.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/queue/client/CloudQueueTests.java @@ -583,10 +583,9 @@ public void testAddMessageLargeVisibilityDelay() throws URISyntaxException, Stor String msgContent = UUID.randomUUID().toString(); final CloudQueueMessage message = new CloudQueueMessage(msgContent); - queue.addMessage(message, 10, 20, null, null); + queue.addMessage(message, 100, 50, null, null); CloudQueueMessage msgFromRetrieve1 = queue.retrieveMessage(); - Assert.assertEquals(message.getMessageContentAsString(), msgContent); - Assert.assertEquals(msgFromRetrieve1.getMessageContentAsString(), msgContent); + Assert.assertNull(msgFromRetrieve1); queue.delete(); } From eea0518ffca79b265f1914bebbad03d534d4046e Mon Sep 17 00:00:00 2001 From: "Justin Yu (Microsoft)" Date: Fri, 19 Oct 2012 17:39:27 -0700 Subject: [PATCH 12/17] Fix jxscl unit tests. --- .../queue/client/CloudQueueClientTests.java | 14 ++++++++------ .../table/client/TableBatchOperationTests.java | 8 +++----- .../services/table/client/TableOperationTests.java | 4 ++-- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/queue/client/CloudQueueClientTests.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/queue/client/CloudQueueClientTests.java index a8405f5abd7f9..3a66842a2bf87 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/queue/client/CloudQueueClientTests.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/queue/client/CloudQueueClientTests.java @@ -120,18 +120,20 @@ public void testListQueuesAndListQueuesSegmentedLargeNumber() throws URISyntaxEx NumberFormat myFormat = NumberFormat.getInstance(); myFormat.setMinimumIntegerDigits(4); - for (int i = 0; i < totalLimit - count; i++) { + for (int i = 0; i < totalLimit - count;) { String sub = myFormat.format(i); CloudQueue q = new CloudQueue(AppendQueueName(httpAcc.getQueueEndpoint(), String.format("listqueue" + sub.replace(",", ""))), qClient); - q.createIfNotExist(); + if (q.createIfNotExist()) + i++; } - } - count = 0; - for (CloudQueue queue : qClient.listQueues()) { - count++; + count = 0; + for (CloudQueue queue : qClient.listQueues()) { + count++; + } } + Assert.assertTrue(count >= totalLimit); ResultSegment segment = qClient.listQueuesSegmented(); diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/table/client/TableBatchOperationTests.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/table/client/TableBatchOperationTests.java index 671ac7c308a75..cab99fc44c303 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/table/client/TableBatchOperationTests.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/table/client/TableBatchOperationTests.java @@ -288,11 +288,9 @@ public void batchReplaceFail() throws StorageException { } catch (TableServiceException ex) { Assert.assertEquals(ex.getMessage(), "Precondition Failed"); - String errorAfterSemiColon = ex.getExtendedErrorInformation().getErrorMessage(); - errorAfterSemiColon = errorAfterSemiColon.substring(errorAfterSemiColon.indexOf(":") + 1); - Assert.assertTrue(errorAfterSemiColon - .startsWith("The condition specified using HTTP conditional header(s) is not met.")); - Assert.assertEquals(ex.getExtendedErrorInformation().getErrorCode(), "ConditionNotMet"); + Assert.assertTrue(ex.getExtendedErrorInformation().getErrorMessage() + .startsWith("The update condition specified in the request was not satisfied.")); + Assert.assertEquals(ex.getExtendedErrorInformation().getErrorCode(), "UpdateConditionNotSatisfied"); } } diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/table/client/TableOperationTests.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/table/client/TableOperationTests.java index 4beb6ebc4ed21..10a6b4734e3a0 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/table/client/TableOperationTests.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/table/client/TableOperationTests.java @@ -517,8 +517,8 @@ public void replaceFail() throws StorageException { catch (TableServiceException ex) { Assert.assertEquals(ex.getMessage(), "Precondition Failed"); Assert.assertTrue(ex.getExtendedErrorInformation().getErrorMessage() - .startsWith("The condition specified using HTTP conditional header(s) is not met.")); - Assert.assertEquals(ex.getExtendedErrorInformation().getErrorCode(), "ConditionNotMet"); + .startsWith("The update condition specified in the request was not satisfied.")); + Assert.assertEquals(ex.getExtendedErrorInformation().getErrorCode(), "UpdateConditionNotSatisfied"); } // delete entity From 333641b67fcb9122ad7cc8a2b131bcc4b63a6d49 Mon Sep 17 00:00:00 2001 From: "Justin Yu (Microsoft)" Date: Fri, 19 Oct 2012 18:22:33 -0700 Subject: [PATCH 13/17] Fix the table unit test. --- .../services/table/client/TableClientTests.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/table/client/TableClientTests.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/table/client/TableClientTests.java index ec05e10cee028..bbb63de1d514f 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/table/client/TableClientTests.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/table/client/TableClientTests.java @@ -503,12 +503,13 @@ public void testTableSASFromPermission() throws StorageException, URISyntaxExcep // Add a policy, check setting and getting. SharedAccessTablePolicy policy1 = new SharedAccessTablePolicy(); Calendar now = GregorianCalendar.getInstance(); + now.add(Calendar.MINUTE, -10); policy1.setSharedAccessStartTime(now.getTime()); - now.add(Calendar.MINUTE, 10); + now.add(Calendar.MINUTE, 30); policy1.setSharedAccessExpiryTime(now.getTime()); - policy1.setPermissions(EnumSet.of(SharedAccessTablePermissions.QUERY, SharedAccessTablePermissions.UPDATE, - SharedAccessTablePermissions.DELETE)); + policy1.setPermissions(EnumSet.of(SharedAccessTablePermissions.ADD, SharedAccessTablePermissions.QUERY, + SharedAccessTablePermissions.UPDATE, SharedAccessTablePermissions.DELETE)); expectedPermissions.getSharedAccessPolicies().put(identifier, policy1); table.uploadPermissions(expectedPermissions); From 000c65839f15eecfa2d0dad518214d0cecd7a96f Mon Sep 17 00:00:00 2001 From: "Justin Yu (Microsoft)" Date: Mon, 22 Oct 2012 16:03:14 -0700 Subject: [PATCH 14/17] Update the AddQueue code to avoid infinite creating the same queue. --- .../services/queue/client/CloudQueueClientTests.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/queue/client/CloudQueueClientTests.java b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/queue/client/CloudQueueClientTests.java index 3a66842a2bf87..68c238dcc9539 100644 --- a/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/queue/client/CloudQueueClientTests.java +++ b/microsoft-azure-api/src/test/java/com/microsoft/windowsazure/services/queue/client/CloudQueueClientTests.java @@ -120,8 +120,8 @@ public void testListQueuesAndListQueuesSegmentedLargeNumber() throws URISyntaxEx NumberFormat myFormat = NumberFormat.getInstance(); myFormat.setMinimumIntegerDigits(4); - for (int i = 0; i < totalLimit - count;) { - String sub = myFormat.format(i); + for (int i = 0, j = 0; i < totalLimit - count; j++) { + String sub = myFormat.format(j); CloudQueue q = new CloudQueue(AppendQueueName(httpAcc.getQueueEndpoint(), String.format("listqueue" + sub.replace(",", ""))), qClient); if (q.createIfNotExist()) From f18f2a4c3cc4d2592a3345946749a5edfdc7fc94 Mon Sep 17 00:00:00 2001 From: Joost de Nijs Date: Mon, 29 Oct 2012 15:50:33 -0700 Subject: [PATCH 15/17] removed emptyline at start of pom.xml --- microsoft-azure-api/pom.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/microsoft-azure-api/pom.xml b/microsoft-azure-api/pom.xml index da5c17510b04f..9dec5ce436d2d 100644 --- a/microsoft-azure-api/pom.xml +++ b/microsoft-azure-api/pom.xml @@ -1,4 +1,3 @@ -