diff --git a/BreakingChanges.txt b/BreakingChanges.txt index 80e1230416341..86b10a399cee1 100644 --- a/BreakingChanges.txt +++ b/BreakingChanges.txt @@ -1,8 +1,14 @@ -Changes in 6.0.0 +Changes in 7.0.0 + +OTHER + * Upgraded Key Vault dependency to 1.0. Users of the IKey and IKeyResolver interfaces should note the interface changes in this version. + * The getErrorCode method of StorageExtendedErrorInformation is now deprecated. Use the corresponding methods in RequestResult and StorageException instead. + +Changes in 6.0.0 FILE -* Many File service APIs can now throw a URISyntaxException. -* Changed listShares() ShareListingDetails parameter to be an enum set like what is done for listing blobs. + * Many File service APIs can now throw a URISyntaxException. + * Changed listShares() ShareListingDetails parameter to be an enum set like what is done for listing blobs. OTHER * DefaultEndpointsProtocol will now be explicitly included in generated connection strings. @@ -10,7 +16,7 @@ OTHER Changes in 5.1.1 OTHER - * Reverted the code from 5.1.0 because it contained a regression and an accidental change. + * Reverted the code from 5.1.0 because it contained a regression and an accidental change. Changes in 5.0.0 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f1ba760c79aa8..23fd16190fadf 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -20,6 +20,7 @@ The Azure Storage development team uses Intellij. However, any preferred IDE or ### Configuration The only step to configure testing is to setup a configuration file or connection string via environment variables. To use the connection string route, create an environment variable named "storageConnection". To use the configuration file route, create an environment variable named "storageTestConfiguration" with the path to a TestConfigurations.xml file with this [template](https://github.com/Azure/azure-storage-java/blob/master/microsoft-azure-storage-test/res/TestConfigurations.xml). +Alternatively, you can fill in microsoft-azure-storage-test/res/TestConfigurations.xml with the appropriate information. ### Running To actually run tests, right click on the test class in the Package Explorer or the individual test in the Outline and select Run As->JUnitTest. All tests or tests grouped by service can be run using the test runners in the com.microsoft.azure.storage package TestRunners file. Running all tests from the top of the package explorer will result in each test being run multiple times as the package explorer will also run every test runner. diff --git a/ChangeLog.txt b/ChangeLog.txt index 082c6b1ee2b99..1a71566fec050 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -1,3 +1,14 @@ +2018.02.05 Version 7.0.0 + * Support for 2017-07-29 REST version. Please see our REST api documentation and blogs for information about the related added features. + * Added support for soft delete feature. If a delete retention policy is enabled through the set service properties API, then blobs or snapshots can be deleted softly and retained for a specified number of days, before being permanently removed by garbage collection. + * When a storage request fails, the error code may now be retrieved directly from the RequestResult and StorageException classes. This error code is populated even in cases where there is no StorageExtendedErrorInformation available, such as in calls to FetchAttributes. + * Queue messages may now be inserted with infinite duration by specifying -1 as the timeToLiveInSeconds parameter to addMessage. + * Improved performance of blob uploadFromFile APIs to avoid unnecessary buffering. + * Improved performance when streaming directly from a FileInputStream to avoid unnecessary buffering. + * Switched to using fixed length streaming mode in the HTTP client to avoid unnecessary buffering. + * Upgraded Key Vault dependency to 1.0. + * Fixed a bug preventing openInputStream from working on a blob snapshot. + 2017.11.01 Version 6.1.0 * Added support for the last time the tier was modified. diff --git a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/AlwaysRetry.java b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/AlwaysRetry.java index 20256c2757c3e..db92fd8ab1438 100644 --- a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/AlwaysRetry.java +++ b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/AlwaysRetry.java @@ -17,8 +17,8 @@ import java.util.ArrayList; import java.util.List; -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertTrue; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; public class AlwaysRetry extends RetryPolicy implements RetryPolicyFactory { diff --git a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/DictionaryKeyResolver.java b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/DictionaryKeyResolver.java index f123d9397214f..760bbee86950d 100644 --- a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/DictionaryKeyResolver.java +++ b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/DictionaryKeyResolver.java @@ -16,9 +16,9 @@ import java.util.HashMap; import java.util.Map; -import java.util.concurrent.Future; -import org.apache.commons.lang3.concurrent.ConcurrentUtils; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.SettableFuture; import com.microsoft.azure.keyvault.core.IKey; import com.microsoft.azure.keyvault.core.IKeyResolver; @@ -32,8 +32,10 @@ public void add(IKey key) } @Override - public Future resolveKeyAsync(String keyId) + public ListenableFuture resolveKeyAsync(String keyId) { - return ConcurrentUtils.constantFuture(this.keys.get(keyId)); + SettableFuture future = SettableFuture.create(); + future.set(this.keys.get(keyId)); + return future; } } diff --git a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/EventFiringTests.java b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/EventFiringTests.java index b42ae25e8d938..6c65dbaa8a2a6 100644 --- a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/EventFiringTests.java +++ b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/EventFiringTests.java @@ -168,16 +168,6 @@ public void testErrorReceivingResponseEvent() throws URISyntaxException, Storage BlobRequestOptions options = new BlobRequestOptions(); options.setRetryPolicyFactory(new RetryNoRetry()); - // setting the sending request event handler to trigger an exception. - // this is a retryable exception - eventContext.getSendingRequestEventHandler().addListener(new StorageEvent() { - @Override - public void eventOccurred(SendingRequestEvent eventArg) { - HttpURLConnection connection = (HttpURLConnection) eventArg.getConnectionObject(); - connection.setFixedLengthStreamingMode(0); - } - }); - eventContext.getErrorReceivingResponseEventHandler().addListener(new StorageEvent() { @Override public void eventOccurred(ErrorReceivingResponseEvent eventArg) { @@ -186,13 +176,15 @@ public void eventOccurred(ErrorReceivingResponseEvent eventArg) { } }); - OperationContext.getGlobalErrorReceivingResponseEventHandler().addListener(new StorageEvent() { + StorageEvent globalEvent = new StorageEvent() { @Override public void eventOccurred(ErrorReceivingResponseEvent eventArg) { assertEquals(eventArg.getRequestResult(), eventArg.getOpContext().getLastResult()); globalCallList.add(true); } - }); + }; + + OperationContext.getGlobalErrorReceivingResponseEventHandler().addListener(globalEvent); CloudBlobClient blobClient = TestHelper.createCloudBlobClient(); CloudBlobContainer container = blobClient.getContainerReference("container1"); @@ -202,10 +194,12 @@ public void eventOccurred(ErrorReceivingResponseEvent eventArg) { CloudBlockBlob blob1 = container.getBlockBlobReference("blob1"); try { String blockID = String.format("%08d", 1); - blob1.uploadBlock(blockID, BlobTestHelper.getRandomDataStream(10), 10, null, options, eventContext); + + // Trigger an error receiving the response by sending more bytes than the stream has. + blob1.uploadBlock(blockID, BlobTestHelper.getRandomDataStream(10), 11, null, options, eventContext); } catch (Exception e) { } - // make sure both the local and globab context update + // make sure both the local and global context update assertEquals(1, callList.size()); assertEquals(1, globalCallList.size()); @@ -214,7 +208,9 @@ public void eventOccurred(ErrorReceivingResponseEvent eventArg) { .setErrorReceivingResponseEventHandler(new StorageEventMultiCaster>()); try { String blockID2 = String.format("%08d", 2); - blob1.uploadBlock(blockID2, BlobTestHelper.getRandomDataStream(10), 10, null, options, eventContext); + + // Trigger an error receiving the response by sending more bytes than the stream has. + blob1.uploadBlock(blockID2, BlobTestHelper.getRandomDataStream(10), 11, null, options, eventContext); } catch (Exception e) { } assertEquals(1, callList.size()); @@ -227,13 +223,17 @@ public void eventOccurred(ErrorReceivingResponseEvent eventArg) { // make sure neither update try { String blockID3 = String.format("%08d", 3); - blob1.uploadBlock(blockID3, BlobTestHelper.getRandomDataStream(10), 10, null, options, eventContext); + + // Trigger an error receiving the response by sending more bytes than the stream has. + blob1.uploadBlock(blockID3, BlobTestHelper.getRandomDataStream(10), 11, null, options, eventContext); } catch (Exception e) { } assertEquals(1, callList.size()); assertEquals(2, globalCallList.size()); } finally { + // Remove the global listener if it wasn't removed already. + OperationContext.getGlobalErrorReceivingResponseEventHandler().removeListener(globalEvent); container.deleteIfExists(); } } @@ -338,7 +338,7 @@ public void eventOccurred(RetryingEvent eventArg) { catch (StorageException e) { assertEquals(HttpURLConnection.HTTP_NOT_FOUND, e.getHttpStatusCode()); assertEquals("The specified container does not exist.", e.getMessage()); - assertEquals(StorageErrorCode.RESOURCE_NOT_FOUND.toString(), e.getErrorCode()); + assertEquals(StorageErrorCodeStrings.CONTAINER_NOT_FOUND, e.getErrorCode()); } assertEquals(1, callList.size()); assertEquals(1, globalCallList.size()); @@ -351,7 +351,7 @@ public void eventOccurred(RetryingEvent eventArg) { catch (StorageException e) { assertEquals(HttpURLConnection.HTTP_NOT_FOUND, e.getHttpStatusCode()); assertEquals("The specified container does not exist.", e.getMessage()); - assertEquals(StorageErrorCode.RESOURCE_NOT_FOUND.toString(), e.getErrorCode()); + assertEquals(StorageErrorCodeStrings.CONTAINER_NOT_FOUND, e.getErrorCode()); } assertEquals(1, callList.size()); assertEquals(2, globalCallList.size()); @@ -368,7 +368,7 @@ public void eventOccurred(RetryingEvent eventArg) { catch (StorageException e) { assertEquals(HttpURLConnection.HTTP_NOT_FOUND, e.getHttpStatusCode()); assertEquals("The specified container does not exist.", e.getMessage()); - assertEquals(StorageErrorCode.RESOURCE_NOT_FOUND.toString(), e.getErrorCode()); + assertEquals(StorageErrorCodeStrings.CONTAINER_NOT_FOUND, e.getErrorCode()); } assertEquals(1, callList.size()); assertEquals(2, globalCallList.size()); @@ -422,7 +422,7 @@ public void eventOccurred(RequestCompletedEvent eventArg) { catch (StorageException e) { assertEquals(HttpURLConnection.HTTP_NOT_FOUND, e.getHttpStatusCode()); assertEquals("The specified container does not exist.", e.getMessage()); - assertEquals(StorageErrorCode.RESOURCE_NOT_FOUND.toString(), e.getErrorCode()); + assertEquals(StorageErrorCodeStrings.CONTAINER_NOT_FOUND, e.getErrorCode()); } assertEquals(2, sendingCallList.size()); diff --git a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/GenericTests.java b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/GenericTests.java index dbf4ce028c7ca..fd7dabfd320f1 100644 --- a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/GenericTests.java +++ b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/GenericTests.java @@ -14,12 +14,8 @@ */ package com.microsoft.azure.storage; -import com.microsoft.azure.storage.blob.BlobOutputStream; -import com.microsoft.azure.storage.blob.BlobRequestOptions; -import com.microsoft.azure.storage.blob.BlobTestHelper; -import com.microsoft.azure.storage.blob.CloudBlobClient; -import com.microsoft.azure.storage.blob.CloudBlobContainer; -import com.microsoft.azure.storage.blob.CloudBlockBlob; +import com.microsoft.azure.storage.blob.*; +import com.microsoft.azure.storage.core.BaseRequest; import com.microsoft.azure.storage.core.SR; import com.microsoft.azure.storage.core.Utility; import com.microsoft.azure.storage.queue.CloudQueue; @@ -476,4 +472,45 @@ private static String generateRandomContainerName() { String containerName = "container" + UUID.randomUUID().toString(); return containerName.replace("-", ""); } + + @Test + public void testErrorCodeFromHeader() throws URISyntaxException, StorageException, IOException { + CloudBlobClient blobClient = TestHelper.createCloudBlobClient(); + CloudBlobContainer container = blobClient.getContainerReference(generateRandomContainerName()); + + CloudAppendBlob appendBlob = container.getAppendBlobReference("testAppend"); + + try { + container.createIfNotExists(); + OperationContext ctx = new OperationContext(); + appendBlob.createOrReplace(); + + // Verify that the error code is set on a non HEAD request + try { + appendBlob.delete(DeleteSnapshotsOption.NONE, AccessCondition.generateIfMatchCondition("garbage"), + null, ctx); + } + catch (Exception e) { + // Validate that the error code is set on the exception and the result + assertEquals(((StorageException)e).getErrorCode(), StorageErrorCodeStrings.CONDITION_NOT_MET); + assertEquals(ctx.getLastResult().getErrorCode(), StorageErrorCodeStrings.CONDITION_NOT_MET); + } + + // Verify that the error code is set on a HEAD request + try { + appendBlob.downloadAttributes(AccessCondition.generateIfMatchCondition("garbage"), null, ctx); + } + catch (Exception e) { + assertEquals(((StorageException)e).getErrorCode(), StorageErrorCodeStrings.CONDITION_NOT_MET); + assertEquals(ctx.getLastResult().getErrorCode(), StorageErrorCodeStrings.CONDITION_NOT_MET); + } + + // Verify that the ErrorCode is not set on a successful request + appendBlob.delete(DeleteSnapshotsOption.NONE, null, null, ctx); + assertEquals(ctx.getLastResult().getErrorCode(), null); + } + finally { + container.deleteIfExists(); + } + } } diff --git a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/ServicePropertiesTests.java b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/ServicePropertiesTests.java index d8221890f83d4..b774a3b11b5e4 100644 --- a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/ServicePropertiesTests.java +++ b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/ServicePropertiesTests.java @@ -35,7 +35,7 @@ import static org.junit.Assert.*; -@Category({ SlowTests.class, DevFabricTests.class, DevStoreTests.class, CloudTests.class }) +@Category({ DevFabricTests.class, DevStoreTests.class, CloudTests.class }) public class ServicePropertiesTests { /** @@ -48,6 +48,7 @@ public class ServicePropertiesTests { public void testAnalyticsDisable() throws StorageException, InterruptedException { ServiceClient client = TestHelper.createCloudBlobClient(); ServiceProperties props = new ServiceProperties(); + props.setDeleteRetentionPolicy(new DeleteRetentionPolicy()); props.setDefaultServiceVersion(Constants.HeaderConstants.TARGET_STORAGE_VERSION); testAnalyticsDisable(client, props); @@ -91,6 +92,7 @@ private void testAnalyticsDisable(ServiceClient client, ServiceProperties props) public void testAnalyticsDefaultServiceVersion() throws StorageException, InterruptedException { ServiceClient client = TestHelper.createCloudBlobClient(); ServiceProperties props = new ServiceProperties(); + props.setDeleteRetentionPolicy(new DeleteRetentionPolicy()); props.setDefaultServiceVersion(Constants.HeaderConstants.TARGET_STORAGE_VERSION); testAnalyticsDefaultServiceVersion(client, props); @@ -151,6 +153,7 @@ private void testAnalyticsDefaultServiceVersion(ServiceClient client, ServicePro public void testAnalyticsLoggingOperations() throws StorageException, InterruptedException { ServiceClient client = TestHelper.createCloudBlobClient(); ServiceProperties props = new ServiceProperties(); + props.setDeleteRetentionPolicy(new DeleteRetentionPolicy()); props.setDefaultServiceVersion(Constants.HeaderConstants.TARGET_STORAGE_VERSION); testAnalyticsLoggingOperations(client, props); @@ -191,6 +194,7 @@ private void testAnalyticsLoggingOperations(ServiceClient client, ServicePropert public void testAnalyticsHourMetricsLevel() throws StorageException, InterruptedException { ServiceClient client = TestHelper.createCloudBlobClient(); ServiceProperties props = new ServiceProperties(); + props.setDeleteRetentionPolicy(new DeleteRetentionPolicy()); props.setDefaultServiceVersion(Constants.HeaderConstants.TARGET_STORAGE_VERSION); testAnalyticsHourMetricsLevel(client, props, null); @@ -259,6 +263,7 @@ private void testAnalyticsHourMetricsLevel( public void testAnalyticsMinuteMetricsLevel() throws StorageException, InterruptedException { ServiceClient client = TestHelper.createCloudBlobClient(); ServiceProperties props = new ServiceProperties(); + props.setDeleteRetentionPolicy(new DeleteRetentionPolicy()); props.setDefaultServiceVersion(Constants.HeaderConstants.TARGET_STORAGE_VERSION); testAnalyticsMinuteMetricsLevel(client, props, null); @@ -327,6 +332,7 @@ private void testAnalyticsMinuteMetricsLevel( public void testAnalyticsRetentionPolicies() throws StorageException, InterruptedException { ServiceClient client = TestHelper.createCloudBlobClient(); ServiceProperties props = new ServiceProperties(); + props.setDeleteRetentionPolicy(new DeleteRetentionPolicy()); props.setDefaultServiceVersion(Constants.HeaderConstants.TARGET_STORAGE_VERSION); testAnalyticsRetentionPolicies(client, props); @@ -397,6 +403,157 @@ private void testAnalyticsRetentionPolicies(ServiceClient client, ServicePropert assertServicePropertiesAreEqual(props, callDownloadServiceProperties(client)); } + /** + * Test delete retention policy for blobs + * + * @throws StorageException + * @throws InterruptedException + */ + @Test + public void testValidDeleteRetentionPolicy() throws StorageException, InterruptedException { + ServiceClient client = TestHelper.createCloudBlobClient(); + + // average setting + testValidDeleteRetentionPolicy(client, true, 5); + + // minimum setting + testValidDeleteRetentionPolicy(client, true, 1); + + // maximum setting + testValidDeleteRetentionPolicy(client, true, 365); + + // disable setting + testValidDeleteRetentionPolicy(client, false, 5); + } + + private void testValidDeleteRetentionPolicy(ServiceClient client, boolean enabled, + Integer interval) + throws StorageException, InterruptedException { + + try { + ServiceProperties expectedServiceProperties = new ServiceProperties(); + expectedServiceProperties.setDeleteRetentionPolicy(new DeleteRetentionPolicy()); + expectedServiceProperties.setDefaultServiceVersion(Constants.HeaderConstants.TARGET_STORAGE_VERSION); + + if (enabled) { + expectedServiceProperties.getDeleteRetentionPolicy().setEnabled(true); + expectedServiceProperties.getDeleteRetentionPolicy().setRetentionIntervalInDays(interval); + callUploadServiceProps(client, expectedServiceProperties, null); + } else { + // interval and retained versions per blob would both be ignored by the service in case the policy is not enabled + ServiceProperties propertiesToUpload = new ServiceProperties(); + propertiesToUpload.setDeleteRetentionPolicy(new DeleteRetentionPolicy()); + propertiesToUpload.getDeleteRetentionPolicy().setRetentionIntervalInDays(interval); + + expectedServiceProperties.getDeleteRetentionPolicy().setEnabled(false); + callUploadServiceProps(client, propertiesToUpload, null); + } + + // verify + assertServicePropertiesAreEqual(expectedServiceProperties, callDownloadServiceProperties(client)); + } + finally { + // reset the delete retention policy + ServiceProperties disabledDeleteRetentionPolicy = new ServiceProperties(); + disabledDeleteRetentionPolicy.setDeleteRetentionPolicy(new DeleteRetentionPolicy()); + callUploadServiceProps(client, disabledDeleteRetentionPolicy, null); + } + } + + /** + * Test invalid delete retention policy for blobs + * + * @throws StorageException + * @throws InterruptedException + */ + @Test + public void testInvalidDeleteRetentionPolicy() throws StorageException, InterruptedException { + ServiceClient client = TestHelper.createCloudBlobClient(); + + // Should not work with 0 days + testInvalidDeleteRetentionPolicy(client, true, 0); + + // Should not work with <0 days + testInvalidDeleteRetentionPolicy(client, true, -1); + + // Should not work with 366 days + testInvalidDeleteRetentionPolicy(client, true, 366); + + + // Should not work with interval as null + testInvalidDeleteRetentionPolicy(client, true, null); + } + + private void testInvalidDeleteRetentionPolicy(ServiceClient client, boolean enabled, + Integer interval) + throws StorageException, InterruptedException { + + // Arrange + ServiceProperties serviceProperties = new ServiceProperties(); + serviceProperties.setDeleteRetentionPolicy(new DeleteRetentionPolicy()); + serviceProperties.setDefaultServiceVersion(Constants.HeaderConstants.TARGET_STORAGE_VERSION); + + if (enabled) { + serviceProperties.getDeleteRetentionPolicy().setEnabled(true); + } + else { + serviceProperties.getDeleteRetentionPolicy().setEnabled(false); + } + serviceProperties.getDeleteRetentionPolicy().setRetentionIntervalInDays(interval); + + // Failure is expected since the retention policy is invalid + try { + callUploadServiceProps(client, serviceProperties, null); + fail("No exception received. An invalid delete retention policy should have raised an exception."); + } + catch (StorageException e) { + assertEquals(e.errorCode, "InvalidXmlDocument"); + } + catch (IllegalArgumentException e) { + assertTrue(e.getMessage().contains("argument must not be null")); + } + catch (Exception e) { + fail("Invalid exception " + e.getClass() + " received when expecting StorageException"); + } + } + + /** + * Test empty delete retention policy for blobs + * + * @throws StorageException + * @throws InterruptedException + */ + @Test + public void testEmptyDeleteRetentionPolicy() throws StorageException, InterruptedException { + ServiceClient client = TestHelper.createCloudBlobClient(); + + try { + // set up initial delete retention policy + ServiceProperties currentServiceProperties = new ServiceProperties(); + currentServiceProperties.setDeleteRetentionPolicy(new DeleteRetentionPolicy()); + currentServiceProperties.setDefaultServiceVersion(Constants.HeaderConstants.TARGET_STORAGE_VERSION); + currentServiceProperties.getDeleteRetentionPolicy().setEnabled(true); + currentServiceProperties.getDeleteRetentionPolicy().setRetentionIntervalInDays(5); + callUploadServiceProps(client, currentServiceProperties, null); + + // verify + assertServicePropertiesAreEqual(currentServiceProperties, callDownloadServiceProperties(client)); + + // try to upload empty retention policy + ServiceProperties emptyServiceProperties = new ServiceProperties(); + callUploadServiceProps(client, emptyServiceProperties, null); + + // verify + assertServicePropertiesAreEqual(currentServiceProperties, callDownloadServiceProperties(client)); + } + finally { + // reset the delete retention policy + ServiceProperties disabledDeleteRetentionPolicy = new ServiceProperties(); + disabledDeleteRetentionPolicy.setDeleteRetentionPolicy(new DeleteRetentionPolicy()); + callUploadServiceProps(client, disabledDeleteRetentionPolicy, null); + } + } + /** * Test CORS with different rules. * @@ -407,6 +564,7 @@ private void testAnalyticsRetentionPolicies(ServiceClient client, ServicePropert public void testCloudValidCorsRules() throws StorageException, InterruptedException { ServiceClient client = TestHelper.createCloudBlobClient(); ServiceProperties props = new ServiceProperties(); + props.setDeleteRetentionPolicy(new DeleteRetentionPolicy()); props.setDefaultServiceVersion(Constants.HeaderConstants.TARGET_STORAGE_VERSION); testCloudValidCorsRules(client, props, null); @@ -530,6 +688,7 @@ private void testCloudValidCorsRules( public void testCorsExpectedExceptions() throws StorageException { ServiceClient client = TestHelper.createCloudBlobClient(); ServiceProperties props = new ServiceProperties(); + props.setDeleteRetentionPolicy(new DeleteRetentionPolicy()); props.setDefaultServiceVersion(Constants.HeaderConstants.TARGET_STORAGE_VERSION); testCorsExpectedExceptions(client, props, null); @@ -593,6 +752,7 @@ private void testCorsExpectedExceptions( public void testCorsMaxOrigins() throws StorageException, InterruptedException { ServiceClient client = TestHelper.createCloudBlobClient(); ServiceProperties props = new ServiceProperties(); + props.setDeleteRetentionPolicy(new DeleteRetentionPolicy()); props.setDefaultServiceVersion(Constants.HeaderConstants.TARGET_STORAGE_VERSION); testCorsMaxOrigins(client, props, null); @@ -644,6 +804,7 @@ private void testCorsMaxOrigins( public void testCorsMaxHeaders() throws StorageException, InterruptedException { ServiceClient client = TestHelper.createCloudBlobClient(); ServiceProperties props = new ServiceProperties(); + props.setDeleteRetentionPolicy(new DeleteRetentionPolicy()); props.setDefaultServiceVersion(Constants.HeaderConstants.TARGET_STORAGE_VERSION); testCorsMaxHeaders(client, props, null); @@ -747,6 +908,7 @@ private void testCorsMaxHeaders( public void testOptionalServiceProperties() throws StorageException, InterruptedException { ServiceClient client = TestHelper.createCloudBlobClient(); ServiceProperties props = new ServiceProperties(); + props.setDeleteRetentionPolicy(new DeleteRetentionPolicy()); props.setDefaultServiceVersion(Constants.HeaderConstants.TARGET_STORAGE_VERSION); testOptionalServiceProperties(client, props); @@ -829,8 +991,9 @@ else if (client.getClass().equals(CloudFileClient.class)) { else { fail(); } - - Thread.sleep(30000); + + // It may take up to 30 seconds for the settings to take effect, but the new properties are immediately + // visible when querying service properties. } private ServiceProperties callDownloadServiceProperties(ServiceClient client) throws StorageException { @@ -867,7 +1030,6 @@ private void testCorsRules(CorsRule rule, ServiceClient client, ServicePropertie } else { CloudFileClient fileClient = ((CloudFileClient) client); fileClient.uploadServiceProperties(fileServiceProperties); - Thread.sleep(30000); assertFileServicePropertiesAreEqual(fileServiceProperties, fileClient.downloadServiceProperties()); } } @@ -891,7 +1053,6 @@ private void testCorsRules(List corsRules, ServiceClient client, Servi } else { CloudFileClient fileClient = ((CloudFileClient) client); fileClient.uploadServiceProperties(fileServiceProperties); - Thread.sleep(30000); assertFileServicePropertiesAreEqual(fileServiceProperties, fileClient.downloadServiceProperties()); } } @@ -974,6 +1135,15 @@ private static void assertServicePropertiesAreEqual(ServiceProperties propsA, Se assertNull(propsA.getCors()); assertNull(propsB.getCors()); } + + if (propsA.getDeleteRetentionPolicy() != null && propsB.getDeleteRetentionPolicy() != null) { + assertEquals(propsA.getDeleteRetentionPolicy().getEnabled(), propsB.getDeleteRetentionPolicy().getEnabled()); + assertEquals(propsA.getDeleteRetentionPolicy().getRetentionIntervalInDays(), propsB.getDeleteRetentionPolicy().getRetentionIntervalInDays()); + } + else { + assertNull(propsA.getDeleteRetentionPolicy()); + assertNull(propsB.getDeleteRetentionPolicy()); + } } /** diff --git a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/TestHelper.java b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/TestHelper.java index 490b8df5baeb1..edcf559722e2e 100644 --- a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/TestHelper.java +++ b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/TestHelper.java @@ -47,8 +47,8 @@ import org.w3c.dom.NodeList; import org.xml.sax.SAXException; -import com.microsoft.azure.keyvault.extensions.RsaKey; -import com.microsoft.azure.keyvault.extensions.SymmetricKey; +import com.microsoft.azure.keyvault.cryptography.RsaKey; +import com.microsoft.azure.keyvault.cryptography.SymmetricKey; import com.microsoft.azure.storage.analytics.CloudAnalyticsClient; import com.microsoft.azure.storage.blob.CloudBlobClient; import com.microsoft.azure.storage.file.CloudFileClient; @@ -64,7 +64,7 @@ public class TestHelper { private static StorageCredentialsAccountAndKey premiumBlobCredentials; private static CloudStorageAccount premiumBlobAccount; - private final static boolean enableFiddler = true; + private final static boolean enableFiddler = false; private final static boolean requireSecondaryEndpoint = false; public static CloudBlobClient createCloudBlobClient() throws StorageException { diff --git a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/TestRunners.java b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/TestRunners.java index 831f4c8d093d8..e0c76c4fae688 100644 --- a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/TestRunners.java +++ b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/TestRunners.java @@ -28,6 +28,7 @@ import com.microsoft.azure.storage.blob.SasTests; import com.microsoft.azure.storage.file.CloudFileClientTests; import com.microsoft.azure.storage.file.CloudFileDirectoryTests; +import com.microsoft.azure.storage.file.CloudFileServerEncryptionTests; import com.microsoft.azure.storage.file.CloudFileShareTests; import com.microsoft.azure.storage.file.CloudFileTests; import com.microsoft.azure.storage.file.FileSasTests; @@ -118,9 +119,9 @@ public static class CoreTestSuite { @RunWith(Suite.class) @SuiteClasses({ BlobOutputStreamTests.class, CloudBlobClientTests.class, CloudBlobContainerTests.class, - CloudBlobDirectoryTests.class, CloudAppendBlobTests.class, CloudBlockBlobTests.class, CloudPageBlobTests.class, - CloudBlobClientEncryptionTests.class, CloudBlobServerEncryptionTests.class, LeaseTests.class, SasTests.class, - PremiumBlobTests.class }) + CloudBlobDirectoryTests.class, CloudAppendBlobTests.class, CloudBlockBlobTests.class, + CloudPageBlobTests.class, CloudBlobClientEncryptionTests.class, CloudBlobServerEncryptionTests.class, + LeaseTests.class, SasTests.class }) public static class BlobTestSuite { } @@ -138,8 +139,8 @@ public static class TableTestSuite { } @RunWith(Suite.class) - @SuiteClasses({ CloudFileClientTests.class, CloudFileDirectoryTests.class, CloudFileShareTests.class, - CloudFileTests.class, FileSasTests.class }) + @SuiteClasses({ CloudFileClientTests.class, CloudFileDirectoryTests.class, CloudFileServerEncryptionTests.class, + CloudFileShareTests.class, CloudFileTests.class, FileSasTests.class }) public static class FileTestSuite { } @@ -184,6 +185,7 @@ public static class FastTestSuite { @RunWith(Categories.class) @IncludeCategory(PremiumBlobTests.class) + @SuiteClasses(AllTestSuite.class) public static class PremiumBlobTestSuite { } } \ No newline at end of file diff --git a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/analytics/CloudAnalyticsClientTests.java b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/analytics/CloudAnalyticsClientTests.java index 822a4f9bfe160..50eeb07c11b35 100644 --- a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/analytics/CloudAnalyticsClientTests.java +++ b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/analytics/CloudAnalyticsClientTests.java @@ -17,7 +17,6 @@ import com.microsoft.azure.storage.Constants; import com.microsoft.azure.storage.StorageException; import com.microsoft.azure.storage.StorageLocation; -import com.microsoft.azure.storage.TestRunners; import com.microsoft.azure.storage.blob.CloudBlobContainer; import com.microsoft.azure.storage.blob.CloudBlockBlob; import com.microsoft.azure.storage.blob.ListBlobItem; @@ -401,7 +400,7 @@ public void testCloudAnalyticsClientParseLogErrors() throws ParseException, URIS CloudAnalyticsClient.parseLogBlobs(null); } catch (IllegalArgumentException e) { - assertEquals(e.getMessage(), "The argument must not be null or an empty string. Argument name: logBlobs."); + assertEquals("The argument must not be null. Argument name: logBlobs.", e.getMessage()); } } diff --git a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/BlobOutputStreamTests.java b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/BlobOutputStreamTests.java index 18556b8a27149..bc4c0a9e612a5 100644 --- a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/BlobOutputStreamTests.java +++ b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/BlobOutputStreamTests.java @@ -20,7 +20,6 @@ import com.microsoft.azure.storage.ResponseReceivedEvent; import com.microsoft.azure.storage.StorageEvent; import com.microsoft.azure.storage.StorageException; -import com.microsoft.azure.storage.TestRunners; import com.microsoft.azure.storage.TestRunners.CloudTests; import com.microsoft.azure.storage.core.SR; diff --git a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/BlobTestHelper.java b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/BlobTestHelper.java index 9daf49ef0d07b..9bbdd0fba35b4 100644 --- a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/BlobTestHelper.java +++ b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/BlobTestHelper.java @@ -14,10 +14,12 @@ */ package com.microsoft.azure.storage.blob; +import com.microsoft.azure.storage.DeleteRetentionPolicy; import com.microsoft.azure.storage.OperationContext; import com.microsoft.azure.storage.StorageCredentials; import com.microsoft.azure.storage.StorageException; import com.microsoft.azure.storage.TestHelper; +import com.microsoft.azure.storage.ServiceProperties; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -436,4 +438,24 @@ public static void assertAreEqual(CopyState copy1, CopyState copy2) { assertEquals(copy1.getTotalBytes(), copy2.getTotalBytes()); } } + + public static void enableSoftDelete() throws StorageException, URISyntaxException, InterruptedException { + ServiceProperties serviceProperties = new ServiceProperties(); + serviceProperties.setDeleteRetentionPolicy(new DeleteRetentionPolicy()); + serviceProperties.getDeleteRetentionPolicy().setEnabled(true); + serviceProperties.getDeleteRetentionPolicy().setRetentionIntervalInDays(3); + + CloudBlobClient bClient = TestHelper.createCloudBlobClient(); + bClient.uploadServiceProperties(serviceProperties); + Thread.sleep(30000); + } + + public static void disableSoftDelete() throws StorageException, URISyntaxException, InterruptedException { + ServiceProperties serviceProperties = new ServiceProperties(); + serviceProperties.setDeleteRetentionPolicy(new DeleteRetentionPolicy()); + + CloudBlobClient bClient = TestHelper.createCloudBlobClient(); + bClient.uploadServiceProperties(serviceProperties); + Thread.sleep(30000); + } } diff --git a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/CloudAppendBlobTests.java b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/CloudAppendBlobTests.java index 5e8988ca04305..53cabff7286a9 100644 --- a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/CloudAppendBlobTests.java +++ b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/CloudAppendBlobTests.java @@ -24,7 +24,6 @@ import com.microsoft.azure.storage.StorageErrorCodeStrings; import com.microsoft.azure.storage.StorageEvent; import com.microsoft.azure.storage.StorageException; -import com.microsoft.azure.storage.TestRunners; import com.microsoft.azure.storage.TestRunners.CloudTests; import com.microsoft.azure.storage.TestRunners.DevFabricTests; @@ -424,25 +423,25 @@ public void testAppendBlobDownloadRangeTest() throws URISyntaxException, ByteArrayOutputStream blobStream = new ByteArrayOutputStream(); try { - blob.downloadRange(0, new Long(0), blobStream); + blob.downloadRange(0, Long.valueOf(0), blobStream); } catch (IndexOutOfBoundsException ex) { } - blob.downloadRange(0, new Long(1024), blobStream); + blob.downloadRange(0, Long.valueOf(1024), blobStream); assertEquals(blobStream.size(), 1024); BlobTestHelper.assertStreamsAreEqualAtIndex(new ByteArrayInputStream( blobStream.toByteArray()), wholeBlob, 0, 0, 1024, 2 * 1024); CloudAppendBlob blob2 = this.container.getAppendBlobReference("blob1"); try { - blob.downloadRange(1024, new Long(0), blobStream); + blob.downloadRange(1024, Long.valueOf(0), blobStream); } catch (IndexOutOfBoundsException ex) { } ByteArrayOutputStream blobStream2 = new ByteArrayOutputStream(); - blob2.downloadRange(1024, new Long(1024), blobStream2); + blob2.downloadRange(1024, Long.valueOf(1024), blobStream2); BlobTestHelper.assertStreamsAreEqualAtIndex(new ByteArrayInputStream( blobStream2.toByteArray()), wholeBlob, 0, 1024, 1024, 2 * 1024); @@ -471,39 +470,39 @@ public void testCloudAppendBlobDownloadRangeToByteArray() .generateRandomBlobNameWithPrefix("downloadrange")); BlobTestHelper.doDownloadRangeToByteArrayTest(blob, 8 * 1024 * 1024, - 8 * 1024 * 1024, 1 * 1024 * 1024, new Long(1 * 1024 * 1024), - new Long(5 * 1024 * 1024)); + 8 * 1024 * 1024, 1 * 1024 * 1024, Long.valueOf(1 * 1024 * 1024), + Long.valueOf(5 * 1024 * 1024)); BlobTestHelper.doDownloadRangeToByteArrayTest(blob, 8 * 1024 * 1024, - 8 * 1024 * 1024, 2 * 1024 * 1024, new Long(2 * 1024 * 1024), - new Long(6 * 1024 * 1024)); + 8 * 1024 * 1024, 2 * 1024 * 1024, Long.valueOf(2 * 1024 * 1024), + Long.valueOf(6 * 1024 * 1024)); BlobTestHelper.doDownloadRangeToByteArrayTest(blob, 8 * 1024 * 1024, - 8 * 1024 * 1024, 1 * 1024 * 1024, new Long(4 * 1024 * 1024), - new Long(4 * 1024 * 1024)); + 8 * 1024 * 1024, 1 * 1024 * 1024, Long.valueOf(4 * 1024 * 1024), + Long.valueOf(4 * 1024 * 1024)); BlobTestHelper.doDownloadRangeToByteArrayTest(blob, 2 * 512, 4 * 512, - 0, new Long(1 * 512), new Long(1 * 512)); + 0, Long.valueOf(1 * 512), Long.valueOf(1 * 512)); BlobTestHelper.doDownloadRangeToByteArrayTest(blob, 2 * 512, 4 * 512, - 1 * 512, new Long(0), null); + 1 * 512, Long.valueOf(0), null); BlobTestHelper.doDownloadRangeToByteArrayTest(blob, 2 * 512, 4 * 512, - 1 * 512, new Long(1 * 512), null); + 1 * 512, Long.valueOf(1 * 512), null); BlobTestHelper.doDownloadRangeToByteArrayTest(blob, 2 * 512, 4 * 512, - 1 * 512, new Long(0), new Long(1 * 512)); + 1 * 512, Long.valueOf(0), Long.valueOf(1 * 512)); BlobTestHelper.doDownloadRangeToByteArrayTest(blob, 2 * 512, 4 * 512, - 2 * 512, new Long(1 * 512), new Long(1 * 512)); + 2 * 512, Long.valueOf(1 * 512), Long.valueOf(1 * 512)); BlobTestHelper.doDownloadRangeToByteArrayTest(blob, 2 * 512, 4 * 512, - 2 * 512, new Long(1 * 512), new Long(2 * 512)); + 2 * 512, Long.valueOf(1 * 512), Long.valueOf(2 * 512)); // Edge cases BlobTestHelper.doDownloadRangeToByteArrayTest(blob, 1024, 1024, 1023, - new Long(1023), new Long(1)); + Long.valueOf(1023), Long.valueOf(1)); BlobTestHelper.doDownloadRangeToByteArrayTest(blob, 1024, 1024, 0, - new Long(1023), new Long(1)); + Long.valueOf(1023), Long.valueOf(1)); BlobTestHelper.doDownloadRangeToByteArrayTest(blob, 1024, 1024, 0, - new Long(0), new Long(1)); + Long.valueOf(0), Long.valueOf(1)); BlobTestHelper.doDownloadRangeToByteArrayTest(blob, 1024, 1024, 0, - new Long(512), new Long(1)); + Long.valueOf(512), Long.valueOf(1)); BlobTestHelper.doDownloadRangeToByteArrayTest(blob, 1024, 1024, 512, - new Long(1023), new Long(1)); + Long.valueOf(1023), Long.valueOf(1)); } @Test diff --git a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/CloudBlobClientEncryptionTests.java b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/CloudBlobClientEncryptionTests.java index 3d30f86410739..c5297ece0b4df 100644 --- a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/CloudBlobClientEncryptionTests.java +++ b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/CloudBlobClientEncryptionTests.java @@ -26,9 +26,11 @@ import java.nio.charset.Charset; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; +import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; import java.util.UUID; import java.util.concurrent.ExecutionException; +import java.util.concurrent.atomic.AtomicInteger; import javax.crypto.Cipher; import javax.crypto.CipherInputStream; @@ -37,15 +39,14 @@ import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; -import org.apache.commons.lang.mutable.MutableInt; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.experimental.categories.Category; import com.microsoft.azure.keyvault.core.IKey; -import com.microsoft.azure.keyvault.extensions.RsaKey; -import com.microsoft.azure.keyvault.extensions.SymmetricKey; +import com.microsoft.azure.keyvault.cryptography.RsaKey; +import com.microsoft.azure.keyvault.cryptography.SymmetricKey; import com.microsoft.azure.storage.Constants; import com.microsoft.azure.storage.DictionaryKeyResolver; import com.microsoft.azure.storage.OperationContext; @@ -156,9 +157,12 @@ public void testDownloadUnencryptedBlobWithEncryptionPolicy() throws StorageExce // Upload data without encryption blob.uploadFromByteArray(msg, 0, msg.length); + // Create an asymmetric encryption key. The provider must be specified to work around an issue in RsaKey. + RsaKey rsaKey = new RsaKey("myKey", 1024, KeyPairGenerator.getInstance("RSA").getProvider()); + // Create options with encryption policy BlobRequestOptions options = new BlobRequestOptions(); - options.setEncryptionPolicy(new BlobEncryptionPolicy(new RsaKey("myKey", 1024), null)); + options.setEncryptionPolicy(new BlobEncryptionPolicy(rsaKey, null)); options.setRequireEncryption(true); try { @@ -605,7 +609,7 @@ else if (type == BlobType.APPEND_BLOB) { // Wait for writes to complete asynchronously Thread.sleep(10000); - // Page blobs have one extra call due to create. + // Page and append blobs have one extra call due to create. if (type == BlobType.BLOCK_BLOB) { assertEquals(1, opContext.getRequestResults().size()); } @@ -622,7 +626,7 @@ else if (type == BlobType.APPEND_BLOB) { // Wait for writes to complete asynchronously Thread.sleep(10000); - // Page blobs have one extra call due to create. + // Page and append blobs have one extra call due to create. if (type == BlobType.BLOCK_BLOB) { assertEquals(2, opContext.getRequestResults().size()); } @@ -635,8 +639,6 @@ else if (type == BlobType.APPEND_BLOB) { // Block blobs have an additional PutBlockList call. assertEquals(4, opContext.getRequestResults().size()); - assertEquals(4, opContext.getRequestResults().size()); - ByteArrayOutputStream downloadedBlob = new ByteArrayOutputStream(); blob.download(downloadedBlob, null, uploadOptions, null); assertArrayEquals(wholeBlob.toByteArray(), downloadedBlob.toByteArray()); @@ -1012,13 +1014,13 @@ private void doEncryptionTestCountOperations(int size, int count, boolean encryp OperationContext opContext = new OperationContext(); - final MutableInt operationCount = new MutableInt(0); + final AtomicInteger operationCount = new AtomicInteger(); opContext.getSendingRequestEventHandler().addListener(new StorageEvent() { @Override public void eventOccurred(SendingRequestEvent eventArg) { - operationCount.increment(); + operationCount.incrementAndGet(); } }); diff --git a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/CloudBlobClientTests.java b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/CloudBlobClientTests.java index 431bf42c2045b..fbf8c881e1858 100644 --- a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/CloudBlobClientTests.java +++ b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/CloudBlobClientTests.java @@ -14,6 +14,9 @@ */ package com.microsoft.azure.storage.blob; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; import java.io.IOException; import java.net.URISyntaxException; import java.util.ArrayList; @@ -35,7 +38,6 @@ 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; import static org.junit.Assert.*; @@ -310,4 +312,53 @@ public void eventOccurred(SendingRequestEvent eventArg) { container.deleteIfExists(); } } + + @Test + @Category({ DevFabricTests.class, DevStoreTests.class, CloudTests.class }) + public void testUploadBlobFromFileSinglePut() throws URISyntaxException, StorageException, IOException { + CloudBlobClient bClient = BlobTestHelper.createCloudBlobClient(); + + final ArrayList callList = new ArrayList(); + OperationContext sendingRequestEventContext = new OperationContext(); + sendingRequestEventContext.getSendingRequestEventHandler().addListener(new StorageEvent() { + + @Override + public void eventOccurred(SendingRequestEvent eventArg) { + assertEquals(eventArg.getRequestResult(), eventArg.getOpContext().getLastResult()); + callList.add(true); + } + }); + + assertEquals(0, callList.size()); + + CloudBlobContainer container = null; + File sourceFile = File.createTempFile("sourceFile", ".tmp"); + try { + container = bClient.getContainerReference(BlobTestHelper.generateRandomContainerName()); + container.createIfNotExists(); + CloudBlockBlob blob = container.getBlockBlobReference(BlobTestHelper + .generateRandomBlobNameWithPrefix("uploadThreshold")); + + sourceFile = File.createTempFile("sourceFile", ".tmp"); + + int fileSize = 10 * 1024; + byte[] buffer = BlobTestHelper.getRandomBuffer(fileSize); + FileOutputStream fos = new FileOutputStream(sourceFile); + fos.write(buffer); + fos.close(); + + // This should make a single call even though FileInputStream is not seekable because of the optimizations + // from wrapping it in a MarkableFileInputStream + blob.upload(new FileInputStream(sourceFile), fileSize - 1, null, null, sendingRequestEventContext); + + assertEquals(1, callList.size()); + } + finally { + container.deleteIfExists(); + + if (sourceFile.exists()) { + sourceFile.delete(); + } + } + } } \ No newline at end of file diff --git a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/CloudBlobContainerTests.java b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/CloudBlobContainerTests.java index c92c628675ddc..e6183e14c2a80 100644 --- a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/CloudBlobContainerTests.java +++ b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/CloudBlobContainerTests.java @@ -19,7 +19,6 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.net.HttpURLConnection; -import java.net.URI; import java.net.URISyntaxException; import java.security.InvalidKeyException; import java.util.Calendar; @@ -34,7 +33,6 @@ import java.util.UUID; import org.junit.After; -import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.experimental.categories.Category; @@ -905,12 +903,12 @@ private static void assertCreatedAndListedBlobsEquivalent(CloudBlockBlob created return; } else { - assertEquals(new Long(length), state2.getBytesCopied()); + assertEquals(Long.valueOf(length), state2.getBytesCopied()); assertNotNull(state2.getCompletionTime()); assertEquals(state1.getCopyId(), state2.getCopyId()); assertNotNull(state2.getSource()); assertEquals(state1.getStatus(), state2.getStatus()); - assertEquals(new Long(length), state2.getTotalBytes()); + assertEquals(Long.valueOf(length), state2.getTotalBytes()); } } } diff --git a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/CloudBlobServerEncryptionTests.java b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/CloudBlobServerEncryptionTests.java index 685b9a89aa3db..7f4517e1f246a 100644 --- a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/CloudBlobServerEncryptionTests.java +++ b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/CloudBlobServerEncryptionTests.java @@ -28,7 +28,6 @@ import org.junit.After; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.experimental.categories.Category; diff --git a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/CloudBlockBlobTests.java b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/CloudBlockBlobTests.java index 978f30359d6b3..c170d1235d954 100644 --- a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/CloudBlockBlobTests.java +++ b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/CloudBlockBlobTests.java @@ -24,7 +24,6 @@ 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; @@ -44,6 +43,7 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URI; import java.net.URISyntaxException; @@ -153,6 +153,381 @@ public void testBlockBlobDelete() throws StorageException, URISyntaxException, I } } + private void assertBlobIsDeleted(CloudBlockBlob blobItem) { + assertTrue(blobItem.deleted); + assertNotNull(blobItem.properties.getDeletedTime()); + assertNotNull(blobItem.properties.getRemainingRetentionDays()); + } + + private void assertBlobNotDeleted(CloudBlockBlob blobItem) { + assertFalse(blobItem.deleted); + assertNull(blobItem.properties.getDeletedTime()); + assertNull(blobItem.properties.getRemainingRetentionDays()); + } + + /** + * Soft-delete a block blob without snapshot. + * + * @throws StorageException + * @throws URISyntaxException + * @throws IOException + */ + @Test + @Category({ DevFabricTests.class, DevStoreTests.class }) + public void testSoftDeleteBlockBlobWithoutSnapshot() throws StorageException, URISyntaxException, IOException, InterruptedException { + try { + BlobTestHelper.enableSoftDelete(); + final CloudBlockBlob blob = this.container.getBlockBlobReference(BlobTestHelper + .generateRandomBlobNameWithPrefix("testBlob")); + + // create + blob.uploadText("text"); + assertTrue(blob.exists()); + assertFalse(blob.deleted); + + // soft-delete + blob.delete(); + assertFalse(blob.exists()); + assertTrue(blob.deleted); + + // deleted blob should be listed + int count = 0; + for (ListBlobItem listBlobItem : this.container.listBlobs(null, true, EnumSet.allOf(BlobListingDetails.class), null, + null)) { + CloudBlockBlob blobItem = (CloudBlockBlob) listBlobItem; + assertEquals(blobItem.getName(), blob.getName()); + assertBlobIsDeleted(blobItem); + count++; + } + assertEquals(count, 1); + + // un-delete the blob so now it should be listed as a normal blob + blob.undelete(); + assertFalse(blob.deleted); + count = 0; + for (ListBlobItem listBlobItem : this.container.listBlobs(null, true, EnumSet.allOf(BlobListingDetails.class), null, + null)) { + CloudBlockBlob blobItem = (CloudBlockBlob) listBlobItem; + assertEquals(blobItem.getName(), blob.getName()); + assertBlobNotDeleted(blobItem); + count++; + } + assertEquals(count, 1); + } + finally { + BlobTestHelper.disableSoftDelete(); + } + } + + /** + * Soft-delete a block blob snapshot. + * + * @throws StorageException + * @throws URISyntaxException + * @throws IOException + */ + @Test + @Category({ DevFabricTests.class, DevStoreTests.class }) + public void testSoftDeleteSingleBlobSnapshot() throws StorageException, URISyntaxException, IOException, InterruptedException { + try { + BlobTestHelper.enableSoftDelete(); + final CloudBlockBlob blob = this.container.getBlockBlobReference(BlobTestHelper + .generateRandomBlobNameWithPrefix("testBlob")); + + // create + blob.uploadText("text"); + assertTrue(blob.exists()); + assertFalse(blob.deleted); + CloudBlob snapshot = blob.createSnapshot(); + assertTrue(snapshot.exists()); + assertFalse(snapshot.deleted); + + // soft-delete the snapshot + snapshot.delete(); + assertFalse(snapshot.exists()); + assertTrue(snapshot.deleted); + + // deleted snapshot should be listed + int count = 0; + for (ListBlobItem listBlobItem : this.container.listBlobs(null, true, EnumSet.allOf(BlobListingDetails.class), null, + null)) { + CloudBlockBlob blobItem = (CloudBlockBlob) listBlobItem; + assertEquals(blobItem.getName(), blob.getName()); + if (!blobItem.isSnapshot()) { + assertBlobNotDeleted(blobItem); + } + else { + assertBlobIsDeleted(blobItem); + } + count++; + } + assertEquals(count, 2); + + // un-delete the snapshot so now its snapshot should be listed as a normal snapshot + blob.undelete(); + count = 0; + for (ListBlobItem listBlobItem : this.container.listBlobs(null, true, EnumSet.allOf(BlobListingDetails.class), null, + null)) { + CloudBlockBlob blobItem = (CloudBlockBlob) listBlobItem; + assertEquals(blobItem.getName(), blob.getName()); + assertBlobNotDeleted(blobItem); + count++; + } + assertEquals(count, 2); + } + finally { + BlobTestHelper.disableSoftDelete(); + } + } + + /** + * Soft-delete only the snapshots of a block blob. + * + * @throws StorageException + * @throws URISyntaxException + * @throws IOException + */ + @Test + @Category({ DevFabricTests.class, DevStoreTests.class }) + public void testSoftDeleteOnlyBlobSnapshots() throws StorageException, URISyntaxException, IOException, InterruptedException { + try { + BlobTestHelper.enableSoftDelete(); + final CloudBlockBlob blob = this.container.getBlockBlobReference(BlobTestHelper + .generateRandomBlobNameWithPrefix("testBlob")); + + // create + blob.uploadText("text"); + assertTrue(blob.exists()); + assertFalse(blob.deleted); + CloudBlob snapshot1 = blob.createSnapshot(); + assertTrue(snapshot1.exists()); + assertFalse(snapshot1.deleted); + CloudBlob snapshot2 = blob.createSnapshot(); + assertTrue(snapshot2.exists()); + assertFalse(snapshot2.deleted); + + // soft-delete the snapshots only + blob.delete(DeleteSnapshotsOption.DELETE_SNAPSHOTS_ONLY, null, null, null); + assertFalse(snapshot1.exists()); + assertFalse(snapshot2.exists()); + + // deleted snapshots should be listed + int count = 0; + for (ListBlobItem listBlobItem : this.container.listBlobs(null, true, EnumSet.allOf(BlobListingDetails.class), null, + null)) { + CloudBlockBlob blobItem = (CloudBlockBlob) listBlobItem; + assertEquals(blobItem.getName(), blob.getName()); + if (!blobItem.isSnapshot()) { + assertBlobNotDeleted(blobItem); + } + else { + assertBlobIsDeleted(blobItem); + } + count++; + } + assertEquals(count, 3); + + // un-delete the snapshots so now they should be listed as a normal snapshots + blob.undelete(); + count = 0; + for (ListBlobItem listBlobItem : this.container.listBlobs(null, true, EnumSet.allOf(BlobListingDetails.class), null, + null)) { + CloudBlockBlob blobItem = (CloudBlockBlob) listBlobItem; + assertEquals(blobItem.getName(), blob.getName()); + assertBlobNotDeleted(blobItem); + count++; + } + assertEquals(count, 3); + } + finally { + BlobTestHelper.disableSoftDelete(); + } + } + + /** + * Soft-delete a block blob along with its snapshots. + * + * @throws StorageException + * @throws URISyntaxException + * @throws IOException + */ + @Test + @Category({ DevFabricTests.class, DevStoreTests.class }) + public void testSoftDeleteBlobIncludingSnapshots() throws StorageException, URISyntaxException, IOException, InterruptedException { + try { + BlobTestHelper.enableSoftDelete(); + final CloudBlockBlob blob = this.container.getBlockBlobReference(BlobTestHelper + .generateRandomBlobNameWithPrefix("testBlob")); + + // create + blob.uploadText("text"); + assertTrue(blob.exists()); + assertFalse(blob.deleted); + CloudBlob snapshot1 = blob.createSnapshot(); + assertTrue(snapshot1.exists()); + assertFalse(snapshot1.deleted); + CloudBlob snapshot2 = blob.createSnapshot(); + assertTrue(snapshot2.exists()); + assertFalse(snapshot2.deleted); + + // soft-delete the blob including the snapshots + blob.delete(DeleteSnapshotsOption.INCLUDE_SNAPSHOTS, null, null, null); + assertFalse(blob.exists()); + assertFalse(snapshot1.exists()); + assertFalse(snapshot2.exists()); + + // deleted snapshots and blob should be listed + int count = 0; + for (ListBlobItem listBlobItem : this.container.listBlobs(null, true, EnumSet.allOf(BlobListingDetails.class), null, + null)) { + CloudBlockBlob blobItem = (CloudBlockBlob) listBlobItem; + assertEquals(blobItem.getName(), blob.getName()); + assertBlobIsDeleted(blobItem); + count++; + } + assertEquals(count, 3); + + // un-delete the blob and snapshots so now they should be listed as a normal blob and snapshots + blob.undelete(); + assertFalse(blob.deleted); + count = 0; + for (ListBlobItem listBlobItem : this.container.listBlobs(null, true, EnumSet.allOf(BlobListingDetails.class), null, + null)) { + CloudBlockBlob blobItem = (CloudBlockBlob) listBlobItem; + assertBlobNotDeleted(blobItem); + count++; + } + assertEquals(count, 3); + } + finally { + BlobTestHelper.disableSoftDelete(); + } + } + + /** + * Soft-delete a block blob which is leased. + * + * @throws StorageException + * @throws URISyntaxException + * @throws IOException + */ + @Test + @Category({ DevFabricTests.class, DevStoreTests.class }) + public void testSoftDeleteBlobWithLease() throws StorageException, URISyntaxException, IOException, InterruptedException { + try { + BlobTestHelper.enableSoftDelete(); + final CloudBlockBlob blob = this.container.getBlockBlobReference(BlobTestHelper + .generateRandomBlobNameWithPrefix("testBlob")); + + // create + blob.uploadText("text"); + assertTrue(blob.exists()); + assertFalse(blob.deleted); + String leaseId = blob.acquireLease(); + + // soft-delete the blob without the lease should fail + try { + blob.delete(DeleteSnapshotsOption.INCLUDE_SNAPSHOTS, null, null, null); + fail("soft-delete the blob without the lease should fail."); + } + catch (StorageException e) { + assertEquals(e.getErrorCode(), "LeaseIdMissing"); + assertFalse(blob.deleted); + } + + // soft-delete the blob with the lease should succeed + AccessCondition accessCondition = new AccessCondition(); + accessCondition.setLeaseID(leaseId); + blob.delete(DeleteSnapshotsOption.NONE, accessCondition, null, null); + assertFalse(blob.exists()); + assertTrue(blob.deleted); + + // deleted blob should be listed + int count = 0; + for (ListBlobItem listBlobItem : this.container.listBlobs(null, true, EnumSet.allOf(BlobListingDetails.class), null, + null)) { + CloudBlockBlob blobItem = (CloudBlockBlob) listBlobItem; + assertEquals(blobItem.getName(), blob.getName()); + assertBlobIsDeleted(blobItem); + count++; + } + assertEquals(count, 1); + + // un-delete the blob so now it should be listed as a normal blob + // also un-deleting the blob gets ride of the lease + blob.undelete(); + count = 0; + for (ListBlobItem listBlobItem : this.container.listBlobs(null, true, EnumSet.allOf(BlobListingDetails.class), null, + null)) { + CloudBlockBlob blobItem = (CloudBlockBlob) listBlobItem; + assertEquals(blobItem.getName(), blob.getName()); + assertBlobNotDeleted(blobItem); + count++; + } + assertEquals(count, 1); + } + finally { + BlobTestHelper.disableSoftDelete(); + } + } + + /** + * Test retained versions per blob on soft-delete. + * + * @throws StorageException + * @throws URISyntaxException + * @throws IOException + */ + @Test + @Category({ DevFabricTests.class, DevStoreTests.class }) + public void testSoftDeleteRetainedVersionsPerBlob() throws StorageException, URISyntaxException, IOException, InterruptedException { + try { + BlobTestHelper.enableSoftDelete(); + final CloudBlockBlob blob = this.container.getBlockBlobReference(BlobTestHelper + .generateRandomBlobNameWithPrefix("testBlob")); + + // create + blob.uploadText("text1"); + assertTrue(blob.exists()); + blob.uploadText("test2"); + blob.uploadText("test3"); + blob.uploadText("test4"); + + // list should show v4 as the current blob, and v1, v2, v3 as soft-deleted snapshots + // so there should be 4 in the list + assertEquals(blob.downloadText(), "test4"); + int count = 0; + for (ListBlobItem listBlobItem : this.container.listBlobs(null, true, EnumSet.allOf(BlobListingDetails.class), null, + null)) { + CloudBlockBlob blobItem = (CloudBlockBlob) listBlobItem; + assertEquals(blobItem.getName(), blob.getName()); + if (blobItem.isSnapshot()) { + assertBlobIsDeleted(blobItem); + } + else { + assertBlobNotDeleted(blobItem); + } + count++; + } + assertEquals(count, 4); + + // un-delete the snapshots so now they should be listed as a normal snapshots + blob.undelete(); + count = 0; + for (ListBlobItem listBlobItem : this.container.listBlobs(null, true, EnumSet.allOf(BlobListingDetails.class), null, + null)) { + CloudBlockBlob blobItem = (CloudBlockBlob) listBlobItem; + assertEquals(blobItem.getName(), blob.getName()); + assertBlobNotDeleted(blobItem); + count++; + } + assertEquals(count, 4); + } + finally { + BlobTestHelper.disableSoftDelete(); + } + } + /** * Test blob name validation. */ @@ -657,6 +1032,10 @@ public void testBlobSnapshotValidationTest() throws StorageException, URISyntaxE byte[] retrievedBuff = outStream.toByteArray(); assertEquals(length, retrievedBuff.length); + InputStream inputStream = blobSnapshot.openInputStream(); + retrievedBuff = inputStream.readAllBytes(); + assertEquals(length, retrievedBuff.length); + // Read operation should work fine. blobSnapshot.downloadAttributes(); @@ -667,6 +1046,11 @@ public void testBlobSnapshotValidationTest() throws StorageException, URISyntaxE blobSnapshotUsingRootUri.download(outStream); retrievedBuff = outStream.toByteArray(); assertEquals(length, retrievedBuff.length); + + inputStream = blobSnapshotUsingRootUri.openInputStream(); + retrievedBuff = inputStream.readAllBytes(); + assertEquals(length, retrievedBuff.length); + assertEquals(blobSnapshot.getSnapshotID(), blobSnapshotUsingRootUri.getSnapshotID()); // Expect an IllegalArgumentException from upload. @@ -707,6 +1091,33 @@ public void testBlobSnapshotValidationTest() throws StorageException, URISyntaxE catch (IllegalArgumentException e) { assertEquals("Cannot perform this operation on a blob representing a snapshot.", e.getMessage()); } + + // Expect an IllegalArgumentException from openOutputStream. + try { + blobSnapshotUsingRootUri.openOutputStream(); + fail("Expect an IllegalArgumentException from openOutputStream"); + } + catch (IllegalArgumentException e) { + assertEquals("Cannot perform this operation on a blob representing a snapshot.", e.getMessage()); + } + + // Expect an IllegalArgumentException from uploadBlock. + try { + blobSnapshotUsingRootUri.uploadBlock("foo", new ByteArrayInputStream(new byte[0]), 0); + fail("Expect an IllegalArgumentException from uploadBlock"); + } + catch (IllegalArgumentException e) { + assertEquals("Cannot perform this operation on a blob representing a snapshot.", e.getMessage()); + } + + // Expect an IllegalArgumentException from commitBlockList. + try { + blobSnapshotUsingRootUri.commitBlockList(new ArrayList()); + fail("Expect an IllegalArgumentException from commitBlockList"); + } + catch (IllegalArgumentException e) { + assertEquals("Cannot perform this operation on a blob representing a snapshot.", e.getMessage()); + } } /** @@ -847,27 +1258,27 @@ public void testBlockBlobDownloadRangeTest() throws URISyntaxException, StorageE ByteArrayOutputStream blobStream = new ByteArrayOutputStream(); try { - blob.downloadRange(0, new Long(0), blobStream, null, null, null); + blob.downloadRange(0, Long.valueOf(0), blobStream, null, null, null); } catch (IndexOutOfBoundsException ex) { } - blob.downloadRange(0, new Long(1024), blobStream); + blob.downloadRange(0, Long.valueOf(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); + blob.downloadRange(1024, Long.valueOf(0), blobStream, null, null, null); } catch (IndexOutOfBoundsException ex) { } ByteArrayOutputStream blobStream2 = new ByteArrayOutputStream(); - blob2.downloadRange(1024, new Long(1024), blobStream2); + blob2.downloadRange(1024, Long.valueOf(1024), blobStream2); BlobTestHelper.assertStreamsAreEqualAtIndex(new ByteArrayInputStream(blobStream2.toByteArray()), wholeBlob, 0, 1024, 1024, 2 * 1024); blob2.downloadAttributes(); @@ -1280,27 +1691,27 @@ public void testCloudBlockBlobDownloadRangeToByteArray() throws URISyntaxExcepti .generateRandomBlobNameWithPrefix("downloadrange")); BlobTestHelper.doDownloadRangeToByteArrayTest(blob, 8 * Constants.MB, 8 * Constants.MB, 1 * Constants.MB, - new Long(1 * Constants.MB), new Long(5 * Constants.MB)); + Long.valueOf(1 * Constants.MB), Long.valueOf(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)); + Long.valueOf(2 * Constants.MB), Long.valueOf(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)); + Long.valueOf(4 * Constants.MB), Long.valueOf(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( + BlobTestHelper.doDownloadRangeToByteArrayTest(blob, 2 * 512, 4 * 512, 0, Long.valueOf(1 * 512), Long.valueOf(1 * 512)); + BlobTestHelper.doDownloadRangeToByteArrayTest(blob, 2 * 512, 4 * 512, 1 * 512, Long.valueOf(0), null); + BlobTestHelper.doDownloadRangeToByteArrayTest(blob, 2 * 512, 4 * 512, 1 * 512, Long.valueOf(1 * 512), null); + BlobTestHelper.doDownloadRangeToByteArrayTest(blob, 2 * 512, 4 * 512, 1 * 512, Long.valueOf(0), Long.valueOf(1 * 512)); + BlobTestHelper.doDownloadRangeToByteArrayTest(blob, 2 * 512, 4 * 512, 2 * 512, Long.valueOf(1 * 512), Long.valueOf( 1 * 512)); - BlobTestHelper.doDownloadRangeToByteArrayTest(blob, 2 * 512, 4 * 512, 2 * 512, new Long(1 * 512), new Long( + BlobTestHelper.doDownloadRangeToByteArrayTest(blob, 2 * 512, 4 * 512, 2 * 512, Long.valueOf(1 * 512), Long.valueOf( 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)); + BlobTestHelper.doDownloadRangeToByteArrayTest(blob, 1024, 1024, 1023, Long.valueOf(1023), Long.valueOf(1)); + BlobTestHelper.doDownloadRangeToByteArrayTest(blob, 1024, 1024, 0, Long.valueOf(1023), Long.valueOf(1)); + BlobTestHelper.doDownloadRangeToByteArrayTest(blob, 1024, 1024, 0, Long.valueOf(0), Long.valueOf(1)); + BlobTestHelper.doDownloadRangeToByteArrayTest(blob, 1024, 1024, 0, Long.valueOf(512), Long.valueOf(1)); + BlobTestHelper.doDownloadRangeToByteArrayTest(blob, 1024, 1024, 512, Long.valueOf(1023), Long.valueOf(1)); } @Test @@ -2021,7 +2432,7 @@ public void testCloudBlockBlobRehydrateBlob() throws StorageException, IOExcepti assertNull(blob2.getProperties().getPremiumPageBlobTier()); assertNotNull(blob2.getProperties().getTierChangeTime()); - Iterator it = this.container.listBlobs().iterator(); + Iterator it = this.container.listBlobs().iterator(); CloudBlockBlob listBlob = (CloudBlockBlob)it.next(); assertEquals(RehydrationStatus.PENDING_TO_COOL, listBlob.getProperties().getRehydrationStatus()); assertEquals(StandardBlobTier.ARCHIVE, listBlob.getProperties().getStandardBlobTier()); diff --git a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/CloudPageBlobTests.java b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/CloudPageBlobTests.java index f6fa2cdc0e395..b2d99141553c4 100644 --- a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/CloudPageBlobTests.java +++ b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/CloudPageBlobTests.java @@ -53,7 +53,6 @@ import com.microsoft.azure.storage.StorageEvent; import com.microsoft.azure.storage.StorageException; import com.microsoft.azure.storage.TestHelper; -import com.microsoft.azure.storage.TestRunners; import com.microsoft.azure.storage.TestRunners.CloudTests; import com.microsoft.azure.storage.TestRunners.DevFabricTests; import com.microsoft.azure.storage.TestRunners.DevStoreTests; @@ -324,27 +323,27 @@ public void testPageBlobDownloadRangeTest() throws URISyntaxException, StorageEx ByteArrayOutputStream blobStream = new ByteArrayOutputStream(); try { - blob.downloadRange(0, new Long(0), blobStream); + blob.downloadRange(0, Long.valueOf(0), blobStream); } catch (IndexOutOfBoundsException ex) { } - blob.downloadRange(0, new Long(1024), blobStream); + blob.downloadRange(0, Long.valueOf(1024), blobStream); assertEquals(blobStream.size(), 1024); BlobTestHelper.assertStreamsAreEqualAtIndex(new ByteArrayInputStream(blobStream.toByteArray()), wholeBlob, 0, 0, 1024, 2 * 1024); CloudPageBlob blob2 = this.container.getPageBlobReference("blob1"); try { - blob.downloadRange(1024, new Long(0), blobStream); + blob.downloadRange(1024, Long.valueOf(0), blobStream); } catch (IndexOutOfBoundsException ex) { } ByteArrayOutputStream blobStream2 = new ByteArrayOutputStream(); - blob2.downloadRange(1024, new Long(1024), blobStream2); + blob2.downloadRange(1024, Long.valueOf(1024), blobStream2); BlobTestHelper.assertStreamsAreEqualAtIndex(new ByteArrayInputStream(blobStream2.toByteArray()), wholeBlob, 0, 1024, 1024, 2 * 1024); @@ -367,27 +366,27 @@ public void testCloudPageBlobDownloadRangeToByteArray() throws URISyntaxExceptio .generateRandomBlobNameWithPrefix("downloadrange")); BlobTestHelper.doDownloadRangeToByteArrayTest(blob, 8 * 1024 * 1024, 8 * 1024 * 1024, 1 * 1024 * 1024, - new Long(1 * 1024 * 1024), new Long(5 * 1024 * 1024)); + Long.valueOf(1 * 1024 * 1024), Long.valueOf(5 * 1024 * 1024)); BlobTestHelper.doDownloadRangeToByteArrayTest(blob, 8 * 1024 * 1024, 8 * 1024 * 1024, 2 * 1024 * 1024, - new Long(2 * 1024 * 1024), new Long(6 * 1024 * 1024)); + Long.valueOf(2 * 1024 * 1024), Long.valueOf(6 * 1024 * 1024)); BlobTestHelper.doDownloadRangeToByteArrayTest(blob, 8 * 1024 * 1024, 8 * 1024 * 1024, 1 * 1024 * 1024, - new Long(4 * 1024 * 1024), new Long(4 * 1024 * 1024)); + Long.valueOf(4 * 1024 * 1024), Long.valueOf(4 * 1024 * 1024)); - 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( + BlobTestHelper.doDownloadRangeToByteArrayTest(blob, 2 * 512, 4 * 512, 0, Long.valueOf(1 * 512), Long.valueOf(1 * 512)); + BlobTestHelper.doDownloadRangeToByteArrayTest(blob, 2 * 512, 4 * 512, 1 * 512, Long.valueOf(0), null); + BlobTestHelper.doDownloadRangeToByteArrayTest(blob, 2 * 512, 4 * 512, 1 * 512, Long.valueOf(1 * 512), null); + BlobTestHelper.doDownloadRangeToByteArrayTest(blob, 2 * 512, 4 * 512, 1 * 512, Long.valueOf(0), Long.valueOf(1 * 512)); + BlobTestHelper.doDownloadRangeToByteArrayTest(blob, 2 * 512, 4 * 512, 2 * 512, Long.valueOf(1 * 512), Long.valueOf( 1 * 512)); - BlobTestHelper.doDownloadRangeToByteArrayTest(blob, 2 * 512, 4 * 512, 2 * 512, new Long(1 * 512), new Long( + BlobTestHelper.doDownloadRangeToByteArrayTest(blob, 2 * 512, 4 * 512, 2 * 512, Long.valueOf(1 * 512), Long.valueOf( 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)); + BlobTestHelper.doDownloadRangeToByteArrayTest(blob, 1024, 1024, 1023, Long.valueOf(1023), Long.valueOf(1)); + BlobTestHelper.doDownloadRangeToByteArrayTest(blob, 1024, 1024, 0, Long.valueOf(1023), Long.valueOf(1)); + BlobTestHelper.doDownloadRangeToByteArrayTest(blob, 1024, 1024, 0, Long.valueOf(0), Long.valueOf(1)); + BlobTestHelper.doDownloadRangeToByteArrayTest(blob, 1024, 1024, 0, Long.valueOf(512), Long.valueOf(1)); + BlobTestHelper.doDownloadRangeToByteArrayTest(blob, 1024, 1024, 512, Long.valueOf(1023), Long.valueOf(1)); } @Test @@ -1286,8 +1285,6 @@ public void testCloudPageBlobSetPremiumBlobTierOnCreate() throws URISyntaxExcept // Test upload from file API File sourceFile = File.createTempFile("sourceFile", ".tmp"); - File destinationFile = new File(sourceFile.getParentFile(), - "destinationFile.tmp"); FileOutputStream fos = new FileOutputStream(sourceFile); fos.write(buffer); fos.close(); @@ -1393,7 +1390,7 @@ public void testCloudPageBlobSetBlobTierOnCopy() throws URISyntaxException, Stor // copy to larger disk CloudPageBlob copy = container.getPageBlobReference("copy"); - String copyId = copy.startCopy(TestHelper.defiddler(source.getUri()), PremiumPageBlobTier.P30, null, null, null, null); + copy.startCopy(TestHelper.defiddler(source.getUri()), PremiumPageBlobTier.P30, null, null, null, null); assertEquals(BlobType.PAGE_BLOB, copy.getProperties().getBlobType()); assertEquals(PremiumPageBlobTier.P30, copy.getProperties().getPremiumPageBlobTier()); assertEquals(PremiumPageBlobTier.P10, source.getProperties().getPremiumPageBlobTier()); @@ -1415,7 +1412,7 @@ public void testCloudPageBlobSetBlobTierOnCopy() throws URISyntaxException, Stor source2.create(1024); CloudPageBlob copy3 = container.getPageBlobReference("copy3"); - String copyId3 = copy3.startCopy(TestHelper.defiddler(source2.getUri()), PremiumPageBlobTier.P60, null ,null ,null, null); + copy3.startCopy(TestHelper.defiddler(source2.getUri()), PremiumPageBlobTier.P60, null ,null ,null, null); assertEquals(BlobType.PAGE_BLOB, copy3.getProperties().getBlobType()); assertEquals(PremiumPageBlobTier.P60, copy3.getProperties().getPremiumPageBlobTier()); assertNull(source2.getProperties().getPremiumPageBlobTier()); diff --git a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/LeaseTests.java b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/LeaseTests.java index 1e2b5a1504b07..82c8efd21ba92 100644 --- a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/LeaseTests.java +++ b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/LeaseTests.java @@ -18,13 +18,12 @@ import com.microsoft.azure.storage.OperationContext; import com.microsoft.azure.storage.StorageErrorCodeStrings; import com.microsoft.azure.storage.StorageException; -import com.microsoft.azure.storage.TestRunners; 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 junit.framework.Assert; +import org.junit.Assert; import org.junit.After; import org.junit.Before; diff --git a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/SasTests.java b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/SasTests.java index 33c05ec26979d..2c4c554b5aa7d 100644 --- a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/SasTests.java +++ b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/blob/SasTests.java @@ -15,8 +15,6 @@ package com.microsoft.azure.storage.blob; -import junit.framework.Assert; - import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -52,14 +50,12 @@ import com.microsoft.azure.storage.SharedAccessAccountPolicy; import com.microsoft.azure.storage.SharedAccessAccountResourceType; import com.microsoft.azure.storage.SharedAccessAccountService; -import com.microsoft.azure.storage.SharedAccessProtocols; import com.microsoft.azure.storage.StorageCredentials; 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.TestHelper; -import com.microsoft.azure.storage.TestRunners; import com.microsoft.azure.storage.TestRunners.CloudTests; import com.microsoft.azure.storage.TestRunners.DevFabricTests; import com.microsoft.azure.storage.TestRunners.DevStoreTests; diff --git a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/file/CloudFileClientTests.java b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/file/CloudFileClientTests.java index 17b38132224f6..b53e8bf8199ae 100644 --- a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/file/CloudFileClientTests.java +++ b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/file/CloudFileClientTests.java @@ -18,7 +18,6 @@ import com.microsoft.azure.storage.ResultContinuation; import com.microsoft.azure.storage.ResultSegment; import com.microsoft.azure.storage.StorageException; -import com.microsoft.azure.storage.TestRunners; import com.microsoft.azure.storage.TestRunners.CloudTests; import com.microsoft.azure.storage.TestRunners.DevFabricTests; import com.microsoft.azure.storage.TestRunners.DevStoreTests; diff --git a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/file/CloudFileDirectoryTests.java b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/file/CloudFileDirectoryTests.java index 92de13ae410f3..eb5ef753b942e 100644 --- a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/file/CloudFileDirectoryTests.java +++ b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/file/CloudFileDirectoryTests.java @@ -21,7 +21,6 @@ import com.microsoft.azure.storage.StorageErrorCodeStrings; import com.microsoft.azure.storage.StorageEvent; import com.microsoft.azure.storage.StorageException; -import com.microsoft.azure.storage.TestRunners; import com.microsoft.azure.storage.TestRunners.CloudTests; import com.microsoft.azure.storage.TestRunners.DevFabricTests; import com.microsoft.azure.storage.TestRunners.DevStoreTests; diff --git a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/file/CloudFileServerEncryptionTests.java b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/file/CloudFileServerEncryptionTests.java index be815738036f8..c512c776dd841 100644 --- a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/file/CloudFileServerEncryptionTests.java +++ b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/file/CloudFileServerEncryptionTests.java @@ -28,7 +28,6 @@ import org.junit.After; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.experimental.categories.Category; diff --git a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/file/CloudFileShareTests.java b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/file/CloudFileShareTests.java index 1153923e4add1..671ce727bfdf0 100644 --- a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/file/CloudFileShareTests.java +++ b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/file/CloudFileShareTests.java @@ -25,7 +25,6 @@ import com.microsoft.azure.storage.TestRunners.DevFabricTests; import com.microsoft.azure.storage.TestRunners.DevStoreTests; import com.microsoft.azure.storage.TestRunners.SlowTests; -import com.microsoft.azure.storage.core.SR; import com.microsoft.azure.storage.core.UriQueryBuilder; import org.junit.After; @@ -626,7 +625,6 @@ public void testListFilesAndDirectoriesWithinShareSnapshot() throws StorageExcep @Test public void testUnsupportedApisShareSnapshot() throws StorageException, URISyntaxException { - CloudFileClient client = FileTestHelper.createCloudFileClient(); this.share.create(); this.share.downloadPermissions(); CloudFileShare snapshot = this.share.createSnapshot(); diff --git a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/file/CloudFileTests.java b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/file/CloudFileTests.java index b12ec11dcb1d3..5832675cb123d 100644 --- a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/file/CloudFileTests.java +++ b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/file/CloudFileTests.java @@ -25,7 +25,6 @@ import com.microsoft.azure.storage.StorageErrorCodeStrings; import com.microsoft.azure.storage.StorageEvent; import com.microsoft.azure.storage.StorageException; -import com.microsoft.azure.storage.TestRunners; import com.microsoft.azure.storage.blob.BlobProperties; import com.microsoft.azure.storage.blob.BlobTestHelper; import com.microsoft.azure.storage.blob.CloudBlob; @@ -40,7 +39,6 @@ import com.microsoft.azure.storage.TestRunners.SlowTests; import com.microsoft.azure.storage.core.SR; import com.microsoft.azure.storage.core.UriQueryBuilder; -import com.microsoft.azure.storage.core.Utility; import org.junit.After; import org.junit.Before; @@ -805,27 +803,27 @@ public void testFileDownloadRangeTest() throws URISyntaxException, StorageExcept ByteArrayOutputStream fileStream = new ByteArrayOutputStream(); try { - file.downloadRange(0, new Long(0), fileStream); + file.downloadRange(0, Long.valueOf(0), fileStream); } catch (IndexOutOfBoundsException ex) { } - file.downloadRange(0, new Long(1024), fileStream); + file.downloadRange(0, Long.valueOf(1024), fileStream); assertEquals(fileStream.size(), 1024); FileTestHelper.assertStreamsAreEqualAtIndex(new ByteArrayInputStream(fileStream.toByteArray()), wholeFile, 0, 0, 1024, 2 * 1024); CloudFile file2 = this.share.getRootDirectoryReference().getFileReference("file1"); try { - file.downloadRange(1024, new Long(0), fileStream); + file.downloadRange(1024, Long.valueOf(0), fileStream); } catch (IndexOutOfBoundsException ex) { } ByteArrayOutputStream fileStream2 = new ByteArrayOutputStream(); - file2.downloadRange(1024, new Long(1024), fileStream2); + file2.downloadRange(1024, Long.valueOf(1024), fileStream2); FileTestHelper.assertStreamsAreEqualAtIndex(new ByteArrayInputStream(fileStream2.toByteArray()), wholeFile, 0, 1024, 1024, 2 * 1024); @@ -848,27 +846,27 @@ public void testCloudFileDownloadRangeToByteArray() throws URISyntaxException, S FileTestHelper.generateRandomFileName()); FileTestHelper.doDownloadRangeToByteArrayTest(file, 8 * 1024 * 1024, 8 * 1024 * 1024, 1 * 1024 * 1024, - new Long(1 * 1024 * 1024), new Long(5 * 1024 * 1024)); + Long.valueOf(1 * 1024 * 1024), Long.valueOf(5 * 1024 * 1024)); FileTestHelper.doDownloadRangeToByteArrayTest(file, 8 * 1024 * 1024, 8 * 1024 * 1024, 2 * 1024 * 1024, - new Long(2 * 1024 * 1024), new Long(6 * 1024 * 1024)); + Long.valueOf(2 * 1024 * 1024), Long.valueOf(6 * 1024 * 1024)); FileTestHelper.doDownloadRangeToByteArrayTest(file, 8 * 1024 * 1024, 8 * 1024 * 1024, 1 * 1024 * 1024, - new Long(4 * 1024 * 1024), new Long(4 * 1024 * 1024)); + Long.valueOf(4 * 1024 * 1024), Long.valueOf(4 * 1024 * 1024)); - FileTestHelper.doDownloadRangeToByteArrayTest(file, 2 * 512, 4 * 512, 0, new Long(1 * 512), new Long(1 * 512)); - FileTestHelper.doDownloadRangeToByteArrayTest(file, 2 * 512, 4 * 512, 1 * 512, new Long(0), null); - FileTestHelper.doDownloadRangeToByteArrayTest(file, 2 * 512, 4 * 512, 1 * 512, new Long(1 * 512), null); - FileTestHelper.doDownloadRangeToByteArrayTest(file, 2 * 512, 4 * 512, 1 * 512, new Long(0), new Long(1 * 512)); - FileTestHelper.doDownloadRangeToByteArrayTest(file, 2 * 512, 4 * 512, 2 * 512, new Long(1 * 512), new Long( + FileTestHelper.doDownloadRangeToByteArrayTest(file, 2 * 512, 4 * 512, 0, Long.valueOf(1 * 512), Long.valueOf(1 * 512)); + FileTestHelper.doDownloadRangeToByteArrayTest(file, 2 * 512, 4 * 512, 1 * 512, Long.valueOf(0), null); + FileTestHelper.doDownloadRangeToByteArrayTest(file, 2 * 512, 4 * 512, 1 * 512, Long.valueOf(1 * 512), null); + FileTestHelper.doDownloadRangeToByteArrayTest(file, 2 * 512, 4 * 512, 1 * 512, Long.valueOf(0), Long.valueOf(1 * 512)); + FileTestHelper.doDownloadRangeToByteArrayTest(file, 2 * 512, 4 * 512, 2 * 512, Long.valueOf(1 * 512), Long.valueOf( 1 * 512)); - FileTestHelper.doDownloadRangeToByteArrayTest(file, 2 * 512, 4 * 512, 2 * 512, new Long(1 * 512), new Long( + FileTestHelper.doDownloadRangeToByteArrayTest(file, 2 * 512, 4 * 512, 2 * 512, Long.valueOf(1 * 512), Long.valueOf( 2 * 512)); // Edge cases - FileTestHelper.doDownloadRangeToByteArrayTest(file, 1024, 1024, 1023, new Long(1023), new Long(1)); - FileTestHelper.doDownloadRangeToByteArrayTest(file, 1024, 1024, 0, new Long(1023), new Long(1)); - FileTestHelper.doDownloadRangeToByteArrayTest(file, 1024, 1024, 0, new Long(0), new Long(1)); - FileTestHelper.doDownloadRangeToByteArrayTest(file, 1024, 1024, 0, new Long(512), new Long(1)); - FileTestHelper.doDownloadRangeToByteArrayTest(file, 1024, 1024, 512, new Long(1023), new Long(1)); + FileTestHelper.doDownloadRangeToByteArrayTest(file, 1024, 1024, 1023, Long.valueOf(1023), Long.valueOf(1)); + FileTestHelper.doDownloadRangeToByteArrayTest(file, 1024, 1024, 0, Long.valueOf(1023), Long.valueOf(1)); + FileTestHelper.doDownloadRangeToByteArrayTest(file, 1024, 1024, 0, Long.valueOf(0), Long.valueOf(1)); + FileTestHelper.doDownloadRangeToByteArrayTest(file, 1024, 1024, 0, Long.valueOf(512), Long.valueOf(1)); + FileTestHelper.doDownloadRangeToByteArrayTest(file, 1024, 1024, 512, Long.valueOf(1023), Long.valueOf(1)); } @Test diff --git a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/file/FileSasTests.java b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/file/FileSasTests.java index 2eec550c97684..8093da24950b6 100644 --- a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/file/FileSasTests.java +++ b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/file/FileSasTests.java @@ -56,7 +56,6 @@ import com.microsoft.azure.storage.StorageException; import com.microsoft.azure.storage.StorageUri; import com.microsoft.azure.storage.TestHelper; -import com.microsoft.azure.storage.TestRunners; import com.microsoft.azure.storage.TestRunners.CloudTests; import com.microsoft.azure.storage.TestRunners.DevFabricTests; import com.microsoft.azure.storage.TestRunners.DevStoreTests; @@ -66,13 +65,9 @@ import com.microsoft.azure.storage.blob.CloudBlobClient; import com.microsoft.azure.storage.blob.CloudBlobContainer; import com.microsoft.azure.storage.blob.CloudBlockBlob; -import com.microsoft.azure.storage.blob.SharedAccessBlobPermissions; -import com.microsoft.azure.storage.blob.SharedAccessBlobPolicy; import com.microsoft.azure.storage.core.PathUtility; import com.microsoft.azure.storage.core.SR; -import junit.framework.Assert; - @Category({ DevFabricTests.class, DevStoreTests.class, CloudTests.class }) public class FileSasTests { diff --git a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/queue/CloudQueueClientGB18030Test.java b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/queue/CloudQueueClientGB18030Test.java index e7591d0b6bfe8..68d8050c24083 100644 --- a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/queue/CloudQueueClientGB18030Test.java +++ b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/queue/CloudQueueClientGB18030Test.java @@ -15,7 +15,6 @@ package com.microsoft.azure.storage.queue; import com.microsoft.azure.storage.StorageException; -import com.microsoft.azure.storage.TestRunners; import com.microsoft.azure.storage.TestRunners.CloudTests; import com.microsoft.azure.storage.TestRunners.DevFabricTests; import com.microsoft.azure.storage.TestRunners.DevStoreTests; diff --git a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/queue/CloudQueueClientTests.java b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/queue/CloudQueueClientTests.java index d6809b6b01b61..0173480e47175 100644 --- a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/queue/CloudQueueClientTests.java +++ b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/queue/CloudQueueClientTests.java @@ -18,7 +18,6 @@ import com.microsoft.azure.storage.ResultSegment; import com.microsoft.azure.storage.StorageException; import com.microsoft.azure.storage.TestHelper; -import com.microsoft.azure.storage.TestRunners; import com.microsoft.azure.storage.core.SR; import org.junit.Test; diff --git a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/queue/CloudQueueEncryptionTests.java b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/queue/CloudQueueEncryptionTests.java index a42a60ce8aa9e..4430b943a95c9 100644 --- a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/queue/CloudQueueEncryptionTests.java +++ b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/queue/CloudQueueEncryptionTests.java @@ -42,8 +42,8 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.microsoft.azure.keyvault.core.IKey; -import com.microsoft.azure.keyvault.extensions.RsaKey; -import com.microsoft.azure.keyvault.extensions.SymmetricKey; +import com.microsoft.azure.keyvault.cryptography.RsaKey; +import com.microsoft.azure.keyvault.cryptography.SymmetricKey; import com.microsoft.azure.storage.DictionaryKeyResolver; import com.microsoft.azure.storage.StorageException; import com.microsoft.azure.storage.TestHelper; diff --git a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/queue/CloudQueueTests.java b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/queue/CloudQueueTests.java index dcea5d8f1c2ec..1b6a984616702 100644 --- a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/queue/CloudQueueTests.java +++ b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/queue/CloudQueueTests.java @@ -24,7 +24,6 @@ import com.microsoft.azure.storage.StorageEvent; import com.microsoft.azure.storage.StorageException; import com.microsoft.azure.storage.TestHelper; -import com.microsoft.azure.storage.TestRunners; import com.microsoft.azure.storage.core.PathUtility; import org.junit.After; @@ -46,27 +45,11 @@ import java.util.TimeZone; import java.util.UUID; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.experimental.categories.Category; - import com.microsoft.azure.keyvault.extensions.Strings; -import com.microsoft.azure.storage.LocationMode; -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.StorageCredentialsSharedAccessSignature; -import com.microsoft.azure.storage.StorageErrorCodeStrings; -import com.microsoft.azure.storage.StorageEvent; -import com.microsoft.azure.storage.StorageException; -import com.microsoft.azure.storage.TestHelper; 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 com.microsoft.azure.storage.core.PathUtility; import static org.junit.Assert.*; @@ -457,8 +440,8 @@ public void testQueueCreateAfterDelete() throws URISyntaxException, StorageExcep catch (StorageException ex) { assertEquals("Expected 409 Exception, QueueBeingDeleted not thrown", ex.getHttpStatusCode(), HttpURLConnection.HTTP_CONFLICT); - assertEquals("Expected 409 Exception, QueueBeingDeleted not thrown", ex.getExtendedErrorInformation() - .getErrorCode(), StorageErrorCodeStrings.QUEUE_BEING_DELETED); + assertEquals("Expected 409 Exception, QueueBeingDeleted not thrown", ex.getErrorCode(), + StorageErrorCodeStrings.QUEUE_BEING_DELETED); } } finally { @@ -528,8 +511,8 @@ public void testQueueCreateIfNotExistsAfterDelete() throws URISyntaxException, S catch (StorageException ex) { assertEquals("Expected 409 Exception, QueueBeingDeleted not thrown", ex.getHttpStatusCode(), HttpURLConnection.HTTP_CONFLICT); - assertEquals("Expected 409 Exception, QueueBeingDeleted not thrown", ex.getExtendedErrorInformation() - .getErrorCode(), StorageErrorCodeStrings.QUEUE_BEING_DELETED); + assertEquals("Expected 409 Exception, QueueBeingDeleted not thrown", ex.getErrorCode(), + StorageErrorCodeStrings.QUEUE_BEING_DELETED); } } finally { @@ -589,8 +572,7 @@ public void testDeleteQueueIfExists() throws URISyntaxException, StorageExceptio catch (StorageException ex) { assertEquals("Expected 409 Exception, QueueBeingDeleted not thrown", ex.getHttpStatusCode(), HttpURLConnection.HTTP_CONFLICT); - assertEquals("Expected 409 Exception, QueueBeingDeleted not thrown", ex.getExtendedErrorInformation() - .getErrorCode(), StorageErrorCodeStrings.QUEUE_BEING_DELETED); + assertEquals("Expected 409 Exception, QueueBeingDeleted not thrown", ex.getErrorCode(), StorageErrorCodeStrings.QUEUE_BEING_DELETED); } } finally { @@ -911,6 +893,9 @@ public void testAddMessageSpecialVisibilityTimeout() throws StorageException { this.queue.addMessage(message, 1, 0, null, null); this.queue.addMessage(message, 7 * 24 * 60 * 60, 0, null, null); this.queue.addMessage(message, 7 * 24 * 60 * 60, 7 * 24 * 60 * 60 - 1, null, null); + this.queue.addMessage(message, 7 * 24 * 60 * 60 * 1024, 0, null, null); + this.queue.addMessage(message, -1, 0, null, null); + this.queue.addMessage(message, 0, 1, null, null); try { this.queue.addMessage(message, 0, -1, null, null); @@ -927,14 +912,14 @@ public void testAddMessageSpecialVisibilityTimeout() throws StorageException { } try { - this.queue.addMessage(message, 7 * 24 * 60 * 60 + 1, 0, null, null); + this.queue.addMessage(message, 0, 7 * 24 * 60 * 60 + 1, null, null); fail(); } catch (final IllegalArgumentException e) { } try { - this.queue.addMessage(message, 0, 7 * 24 * 60 * 60 + 1, null, null); + this.queue.addMessage(message, -2, 0, null, null); fail(); } catch (final IllegalArgumentException e) { diff --git a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/table/TableBatchOperationTests.java b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/table/TableBatchOperationTests.java index dfa1e47b6cefa..630bb5f9683d5 100644 --- a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/table/TableBatchOperationTests.java +++ b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/table/TableBatchOperationTests.java @@ -322,7 +322,7 @@ public void testBatchAddNullShouldThrow() { fail(); } catch (IllegalArgumentException ex) { - assertEquals(ex.getMessage(), String.format(SR.ARGUMENT_NULL_OR_EMPTY, "element")); + assertEquals(String.format(SR.ARGUMENT_NULL, "element"), ex.getMessage()); } } @@ -334,8 +334,8 @@ public void testBatchRetrieveWithNullResolverShouldThrow() { fail(); } catch (IllegalArgumentException ex) { - assertEquals(ex.getMessage(), - String.format(SR.ARGUMENT_NULL_OR_EMPTY, SR.QUERY_REQUIRES_VALID_CLASSTYPE_OR_RESOLVER)); + assertEquals(String.format(SR.ARGUMENT_NULL, SR.QUERY_REQUIRES_VALID_CLASSTYPE_OR_RESOLVER), + ex.getMessage()); } } diff --git a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/table/TableClientTests.java b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/table/TableClientTests.java index 78e9f81812777..4aa3c5ddb82c7 100644 --- a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/table/TableClientTests.java +++ b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/table/TableClientTests.java @@ -24,7 +24,6 @@ import java.util.Date; import java.util.EnumSet; import java.util.GregorianCalendar; -import java.util.Locale; import java.util.UUID; import org.junit.Test; @@ -566,7 +565,7 @@ public void testTableSASPkRk() throws StorageException, URISyntaxException, Inva } catch (StorageException e) { assertEquals(HttpURLConnection.HTTP_FORBIDDEN, e.getHttpStatusCode()); - assertEquals("AuthorizationFailure", e.getExtendedErrorInformation().getErrorCode()); + assertEquals("AuthorizationFailure", e.getErrorCode()); } ent = new Class1("javatables_batch_1", "05"); @@ -576,7 +575,7 @@ public void testTableSASPkRk() throws StorageException, URISyntaxException, Inva } catch (StorageException e) { assertEquals(HttpURLConnection.HTTP_FORBIDDEN, e.getHttpStatusCode()); - assertEquals("AuthorizationFailure", e.getExtendedErrorInformation().getErrorCode()); + assertEquals("AuthorizationFailure", e.getErrorCode()); } } diff --git a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/table/TableEncryptionTests.java b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/table/TableEncryptionTests.java index 8e2afe9e520ce..f4bee5df7dcfc 100644 --- a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/table/TableEncryptionTests.java +++ b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/table/TableEncryptionTests.java @@ -32,7 +32,9 @@ import org.junit.Before; import org.junit.Test; -import com.microsoft.azure.keyvault.extensions.SymmetricKey; +import com.google.common.io.BaseEncoding; + +import com.microsoft.azure.keyvault.cryptography.SymmetricKey; import com.microsoft.azure.storage.Constants; import com.microsoft.azure.storage.DictionaryKeyResolver; import com.microsoft.azure.storage.ResultContinuation; @@ -45,7 +47,6 @@ import com.microsoft.azure.storage.table.TableTestHelper.Class1; import com.microsoft.azure.storage.table.TableTestHelper.EncryptedClass1; import com.microsoft.azure.storage.table.TableTestHelper.ComplexEntity; -import com.sun.jersey.core.util.Base64; public class TableEncryptionTests { CloudTable table = null; @@ -1096,27 +1097,27 @@ public void testCrossPlatformCompatibility() throws StorageException, URISyntaxE // Hard code some sample data, then see if we can decrypt it. // This key is used only for test, do not use to encrypt any sensitive data. - SymmetricKey sampleKEK = new SymmetricKey("key1", Base64.decode("rFz7+tv4hRiWdWUJMFlxl1xxtU/qFUeTriGaxwEcxjU=")); + SymmetricKey sampleKEK = new SymmetricKey("key1", BaseEncoding.base64().decode("rFz7+tv4hRiWdWUJMFlxl1xxtU/qFUeTriGaxwEcxjU=")); // This data here was created using Fiddler to capture the .NET library uploading an encrypted entity, encrypted with the specified KEK and CEK. // Note that this data is lacking the library information in the KeyWrappingMetadata. DynamicTableEntity dteNetOld = new DynamicTableEntity("pk", "netUp"); - dteNetOld.getProperties().put("sampleProp", new EntityProperty(Base64.decode("27cLSlSFqy9C0xUCr57XAA=="))); - dteNetOld.getProperties().put("sampleProp2", new EntityProperty(Base64.decode("pZR6Ln/DwbwyyOCEezL/hg=="))); - dteNetOld.getProperties().put("sampleProp3", new EntityProperty(Base64.decode("JOix4N8eX/WuCtIvlD2QxQ=="))); + dteNetOld.getProperties().put("sampleProp", new EntityProperty(BaseEncoding.base64().decode("27cLSlSFqy9C0xUCr57XAA=="))); + dteNetOld.getProperties().put("sampleProp2", new EntityProperty(BaseEncoding.base64().decode("pZR6Ln/DwbwyyOCEezL/hg=="))); + dteNetOld.getProperties().put("sampleProp3", new EntityProperty(BaseEncoding.base64().decode("JOix4N8eX/WuCtIvlD2QxQ=="))); dteNetOld.getProperties().put("_ClientEncryptionMetadata1", new EntityProperty("{\"WrappedContentKey\":{\"KeyId\":\"key1\",\"EncryptedKey\":\"pwSKxpJkwCS2zCaykh0m8e4OApeLuQ4FiahZ9zdwxaLL1HsWqQ4DSw==\",\"Algorithm\":\"A256KW\"},\"EncryptionAgent\":{\"Protocol\":\"1.0\",\"EncryptionAlgorithm\":\"AES_CBC_256\"},\"ContentEncryptionIV\":\"obTAQcYeFQ3IU7Jfcema7Q==\",\"KeyWrappingMetadata\":{}}")); - dteNetOld.getProperties().put("_ClientEncryptionMetadata2", new EntityProperty(Base64.decode("MWA7LlvXSJnKhf8f7MVhfjWECkxrCyCXGIlYY6ucpr34IVDU7fN6IHvKxV15WiXp"))); + dteNetOld.getProperties().put("_ClientEncryptionMetadata2", new EntityProperty(BaseEncoding.base64().decode("MWA7LlvXSJnKhf8f7MVhfjWECkxrCyCXGIlYY6ucpr34IVDU7fN6IHvKxV15WiXp"))); testTable.execute(TableOperation.insert(dteNetOld)); // This data here was created using Fiddler to capture the Java library uploading an encrypted entity, encrypted with the specified KEK and CEK. // Note that this data is lacking the KeyWrappingMetadata. It also constructs an IV with PK + RK + column name. DynamicTableEntity dteJavaOld = new DynamicTableEntity("pk", "javaUp"); - dteJavaOld.getProperties().put("sampleProp", new EntityProperty(Base64.decode("sa3bCvXq79ImSPveChS+cg=="))); - dteJavaOld.getProperties().put("sampleProp2", new EntityProperty(Base64.decode("KXjuBNn9DesCmMcdVpamJw=="))); - dteJavaOld.getProperties().put("sampleProp3", new EntityProperty(Base64.decode("wykVEni1rV+H6oNjoNml6A=="))); + dteJavaOld.getProperties().put("sampleProp", new EntityProperty(BaseEncoding.base64().decode("sa3bCvXq79ImSPveChS+cg=="))); + dteJavaOld.getProperties().put("sampleProp2", new EntityProperty(BaseEncoding.base64().decode("KXjuBNn9DesCmMcdVpamJw=="))); + dteJavaOld.getProperties().put("sampleProp3", new EntityProperty(BaseEncoding.base64().decode("wykVEni1rV+H6oNjoNml6A=="))); dteJavaOld.getProperties().put("_ClientEncryptionMetadata1", new EntityProperty("{\"WrappedContentKey\":{\"KeyId\":\"key1\",\"EncryptedKey\":\"2F4rIuDmGPgEmhpvTtE7x6281BetKz80EsgRwGxTjL8rRt7Z7GrOgg==\",\"Algorithm\":\"A256KW\"},\"EncryptionAgent\":{\"Protocol\":\"1.0\",\"EncryptionAlgorithm\":\"AES_CBC_256\"},\"ContentEncryptionIV\":\"8st/uXffG+6DxBhw4D1URw==\"}")); - dteJavaOld.getProperties().put("_ClientEncryptionMetadata2", new EntityProperty(Base64.decode("WznUoytxkvl9KhZ4mNlqkBvRTUHN/D5IgJmNl7kQBOtFBOSgZZrTfZXKH8GjmvKA"))); + dteJavaOld.getProperties().put("_ClientEncryptionMetadata2", new EntityProperty(BaseEncoding.base64().decode("WznUoytxkvl9KhZ4mNlqkBvRTUHN/D5IgJmNl7kQBOtFBOSgZZrTfZXKH8GjmvKA"))); testTable.execute(TableOperation.insert(dteJavaOld)); diff --git a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/table/TableOperationTests.java b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/table/TableOperationTests.java index 48de0b766dc3f..32b080cef2303 100644 --- a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/table/TableOperationTests.java +++ b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/table/TableOperationTests.java @@ -83,8 +83,8 @@ public void testRetrieveWithNullResolver() { TableOperation.retrieve("foo", "blah", (EntityResolver) null); } catch (IllegalArgumentException ex) { - assertEquals(ex.getMessage(), - String.format(SR.ARGUMENT_NULL_OR_EMPTY, SR.QUERY_REQUIRES_VALID_CLASSTYPE_OR_RESOLVER)); + assertEquals(String.format(SR.ARGUMENT_NULL, SR.QUERY_REQUIRES_VALID_CLASSTYPE_OR_RESOLVER), + ex.getMessage()); } } diff --git a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/table/TableQueryTests.java b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/table/TableQueryTests.java index ceb1176ebd366..ab0712eb5413d 100644 --- a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/table/TableQueryTests.java +++ b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/table/TableQueryTests.java @@ -82,7 +82,7 @@ public void testQueryWithNullClassType() { TableQuery.from(null); } catch (IllegalArgumentException ex) { - assertEquals(ex.getMessage(), String.format(SR.ARGUMENT_NULL_OR_EMPTY, "class type")); + assertEquals(String.format(SR.ARGUMENT_NULL, "class type"), ex.getMessage()); } } diff --git a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/table/TableTestHelper.java b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/table/TableTestHelper.java index bee9610e4a710..86a4f6eca9ccb 100644 --- a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/table/TableTestHelper.java +++ b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/table/TableTestHelper.java @@ -383,14 +383,14 @@ public void populateEntity() { // this.setNegativeZeroValuePrimDouble(-0.0); // set objects - this.setRegularDouble(new Double(5)); + this.setRegularDouble(Double.valueOf(5)); this.setNanDouble(Double.NaN); this.setPositiveInfinityDouble(Double.POSITIVE_INFINITY); this.setNegativeInfinityDouble(Double.NEGATIVE_INFINITY); this.setMinValueDouble(Double.MIN_VALUE); this.setMaxValueDouble(Double.MAX_VALUE); - this.setMinExponentValueDouble(new Double(Double.MIN_EXPONENT)); - this.setMaxExponentValueDouble(new Double(Double.MAX_EXPONENT)); + this.setMinExponentValueDouble(Double.valueOf(Double.MIN_EXPONENT)); + this.setMaxExponentValueDouble(Double.valueOf(Double.MAX_EXPONENT)); this.setMinNormalValueDouble(Double.MIN_NORMAL); this.setZeroValueDouble(0.0); // this.setNegativeZeroValueDouble(-0.0); diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/Constants.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/Constants.java index 69d0d52124991..218071f30bab5 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/Constants.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/Constants.java @@ -223,6 +223,10 @@ public static class AnalyticsConstants { */ public static final String WRITE_ELEMENT = "Write"; + /** + * The XML element for the delete retention policy. + */ + public static final String DELETE_RETENTION_POLICY_ELEMENT = "DeleteRetentionPolicy"; } /** @@ -450,6 +454,11 @@ public static class HeaderConstants { */ public static final String ETAG = "ETag"; + /** + * The ErrorCode header. + */ + public static final String ERROR_CODE = "x-ms-error-code"; + /** * An unused HTTP code used internally to indicate a non-http related failure when constructing * {@link StorageException} objects @@ -641,7 +650,7 @@ public static class HeaderConstants { /** * The current storage version header value. */ - public static final String TARGET_STORAGE_VERSION = "2017-04-17"; + public static final String TARGET_STORAGE_VERSION = "2017-07-29"; /** * The header that specifies the next visible time for a queue message. @@ -661,7 +670,7 @@ public static class HeaderConstants { /** * Specifies the value to use for UserAgent header. */ - public static final String USER_AGENT_VERSION = "6.1.0"; + public static final String USER_AGENT_VERSION = "7.0.0"; /** * The default type for content-type and accept @@ -818,6 +827,11 @@ public static class QueryConstants { */ public static final String SHARE_SNAPSHOT = "sharesnapshot"; + /** + * The query component for un-delete operation. + */ + public static final String UNDELETE = "undelete"; + /** * The query component for the SAS start partition key. */ diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/DeleteRetentionPolicy.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/DeleteRetentionPolicy.java new file mode 100644 index 0000000000000..738968a1c49dc --- /dev/null +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/DeleteRetentionPolicy.java @@ -0,0 +1,67 @@ +/** + * 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; + +/** + * Represents the policy governing the retention of deleted blobs. + */ +public class DeleteRetentionPolicy { + /** + * Indicates whether a deleted blob or snapshot is retained or immediately removed by a delete operation. + */ + private boolean enabled = false; + + /** + * Required only if Enabled is true. Indicates the number of days that deleted blobs are retained. + * All data older than this value will be permanently deleted. + * The minimum value you can specify is 1; the largest value is 365. + */ + private Integer retentionIntervalInDays; + + /** + * Return a boolean indicating whether the DeleteRetentionPolicy is enabled. + * + * @return A boolean indicating whether a deleted blob or snapshot is retained or immediately removed by a delete operation. + */ + public boolean getEnabled() { + return this.enabled; + } + + /** + * Get the retention interval(in days) of the DeleteRetentionPolicy. + * + * @return An Integer which contains the retention interval. + */ + public Integer getRetentionIntervalInDays() { + return this.retentionIntervalInDays; + } + + /** + * Set a boolean indicating whether the DeleteRetentionPolicy is enabled. + * @param enabled indicates whether the DeleteRetentionPolicy is enabled. + */ + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + /** + * Set the retention interval in days for the delete retention policy. + * @param retentionIntervalInDays represents the number of days that a deleted blob is retained. + */ + public void setRetentionIntervalInDays(final Integer retentionIntervalInDays) { + this.retentionIntervalInDays = retentionIntervalInDays; + } +} diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/RequestResult.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/RequestResult.java index 774301975a6f3..ec9d78f067575 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/RequestResult.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/RequestResult.java @@ -67,6 +67,11 @@ public final class RequestResult { */ private String statusMessage; + /** + * Represents the service ErrorCode for the request. + */ + private String errorCode; + /** * Represents the stop date of the operation. */ @@ -167,6 +172,12 @@ public String getStatusMessage() { return this.statusMessage; } + /** + * Gets the service ErrorCode for the request. + * + * @return A String which contains the service ErrorCode. + */ + public String getErrorCode() { return this.errorCode; } /** * Gets the stop date for the request. * @@ -273,6 +284,14 @@ public void setStatusMessage(final String statusMessage) { this.statusMessage = statusMessage; } + /** + * Sets the service ErrorCode for the request. + * + * @param errorCode + * A String which contains the service ErrorCode to set. + */ + public void setErrorCode(final String errorCode) { this.errorCode = errorCode; } + /** * Sets the stop date for the request. * diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/ResultSegment.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/ResultSegment.java index b828bfa47c955..3b76981b41d1b 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/ResultSegment.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/ResultSegment.java @@ -84,7 +84,7 @@ public boolean getHasMoreResults() { * @return true if the page has more results; otherwise, false. */ public boolean getIsPageComplete() { - return (new Integer(this.length)).equals(this.pageSize); + return Integer.valueOf(this.length).equals(this.pageSize); } /** diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/ServiceProperties.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/ServiceProperties.java index 3c8d3d26c7e1b..2336ef295168b 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/ServiceProperties.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/ServiceProperties.java @@ -45,6 +45,11 @@ public final class ServiceProperties { */ private String defaultServiceVersion; + /** + * Specifies whether to retain deleted blobs for a certain period of time or remove them permanently when being deleted. + */ + private DeleteRetentionPolicy deleteRetentionPolicy; + /** * Initializes a new instances of the ServiceProperties class. */ @@ -149,4 +154,20 @@ public String getDefaultServiceVersion() { public void setDefaultServiceVersion(final String defaultServiceVersion) { this.defaultServiceVersion = defaultServiceVersion; } + + /** + * Set the delete retention policy for blobs. + * @param deleteRetentionPolicy is the policy which indicates whether to retain deleted blobs for a period of time. + */ + public void setDeleteRetentionPolicy(DeleteRetentionPolicy deleteRetentionPolicy) { + this.deleteRetentionPolicy = deleteRetentionPolicy; + } + + /** + * Get the delete retention policy. + * @return the policy which indicates whether to retain deleted blobs for a period of time. + */ + public DeleteRetentionPolicy getDeleteRetentionPolicy() { + return deleteRetentionPolicy; + } } diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/ServicePropertiesHandler.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/ServicePropertiesHandler.java index ec370d5df2ef9..0678c5a21d90e 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/ServicePropertiesHandler.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/ServicePropertiesHandler.java @@ -62,6 +62,7 @@ public static ServiceProperties readServicePropertiesFromStream(final InputStrea handler.props.setHourMetrics(null); handler.props.setMinuteMetrics(null); handler.props.setCors(null); + handler.props.setDeleteRetentionPolicy(null); saxParser.parse(stream, handler); return handler.props; @@ -86,6 +87,9 @@ else if (Constants.AnalyticsConstants.MINUTE_METRICS_ELEMENT.equals(localName)) else if (Constants.AnalyticsConstants.CORS_ELEMENT.equals(localName)) { this.props.setCors(new CorsProperties()); } + else if (Constants.AnalyticsConstants.DELETE_RETENTION_POLICY_ELEMENT.equals(localName)) { + this.props.setDeleteRetentionPolicy(new DeleteRetentionPolicy()); + } } @Override @@ -211,6 +215,24 @@ else if (Constants.AnalyticsConstants.MAX_AGE_IN_SECONDS_ELEMENT.equals(currentN this.rule.setMaxAgeInSeconds(Integer.parseInt(value)); } } + else if (Constants.AnalyticsConstants.DELETE_RETENTION_POLICY_ELEMENT.equals(parentNode)) { + if (Constants.AnalyticsConstants.ENABLED_ELEMENT.equals(currentNode)) { + if (value != null) { + boolean enabled = Boolean.parseBoolean(value); + if (enabled) { + this.props.getDeleteRetentionPolicy().setEnabled(true); + } + else { + this.props.getDeleteRetentionPolicy().setEnabled(false); + } + } + } + else if (Constants.AnalyticsConstants.DAYS_ELEMENT.equals(currentNode)) { + if (value != null) { + this.props.getDeleteRetentionPolicy().setRetentionIntervalInDays(Integer.parseInt(value)); + } + } + } this.bld = new StringBuilder(); } diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/ServicePropertiesSerializer.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/ServicePropertiesSerializer.java index 49ea4b9c1b3af..d5a99962e6bf3 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/ServicePropertiesSerializer.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/ServicePropertiesSerializer.java @@ -32,8 +32,8 @@ final class ServicePropertiesSerializer { /** * Writes the contents of the ServiceProperties to the stream in xml format. * - * @param opContext - * a tracking object for the request + * @param properties + * the ServiceProperties object to write. * @return a byte array of the content to write to the stream. * @throws XMLStreamException * if there is an error writing content to the stream. @@ -76,6 +76,11 @@ public static byte[] serializeToByteArray(final ServiceProperties properties) th xmlw.writeEndElement(); } + // Delete retention policy + if (properties.getDeleteRetentionPolicy() != null) { + writeDeleteRetentionPolicy(xmlw, properties.getDeleteRetentionPolicy()); + } + // end StorageServiceProperties xmlw.writeEndElement(); @@ -90,6 +95,35 @@ public static byte[] serializeToByteArray(final ServiceProperties properties) th } } + /** + * Writes the given delete retention policy to the XMLStreamWriter. + * + * @param xmlw + * the XMLStreamWriter to write to. + * @param deleteRetentionPolicy + * the delete retention policy to write + * @throws XMLStreamException + */ + private static void writeDeleteRetentionPolicy(final XMLStreamWriter xmlw, final DeleteRetentionPolicy deleteRetentionPolicy) throws XMLStreamException { + // deleteRetentionPolicy + xmlw.writeStartElement(Constants.AnalyticsConstants.DELETE_RETENTION_POLICY_ELEMENT); + + // Enabled + xmlw.writeStartElement(Constants.AnalyticsConstants.ENABLED_ELEMENT); + xmlw.writeCharacters(deleteRetentionPolicy.getEnabled() ? Constants.TRUE : Constants.FALSE); + xmlw.writeEndElement(); + + if (deleteRetentionPolicy.getEnabled()) { + // Include retention days and retained versions per blob + xmlw.writeStartElement(Constants.AnalyticsConstants.DAYS_ELEMENT); + xmlw.writeCharacters(deleteRetentionPolicy.getRetentionIntervalInDays().toString()); + xmlw.writeEndElement(); + } + + // end deleteRetentionPolicy + xmlw.writeEndElement(); + } + /** * Writes the retention policy to the XMLStreamWriter. * @@ -217,7 +251,7 @@ private static void writeMetricsProperties(final XMLStreamWriter xmlw, final Met * * @param xmlw * the XMLStreamWriter to write to. - * @param cors + * @param logging * the logging properties to be written. * @throws XMLStreamException */ diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/StorageException.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/StorageException.java index 5d36498a00d27..c3f0a42a3b197 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/StorageException.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/StorageException.java @@ -84,96 +84,11 @@ public static StorageException translateException(final StorageRequest } StorageExtendedErrorInformation extendedError = request.parseErrorDetails(); - if (extendedError != null) { - // 1. If extended information is available use it - translatedException = new StorageException(extendedError.getErrorCode(), responseMessage, responseCode, - extendedError, cause); - } else { - // 2. If extended information is unavailable, translate exception based - // on status code - translatedException = translateFromHttpStatus(responseCode, responseMessage, cause); - } - - if (translatedException != null) { - Utility.logHttpError(translatedException, opContext); - return translatedException; - } else { - return new StorageException(StorageErrorCode.SERVICE_INTERNAL_ERROR.toString(), - "The server encountered an unknown failure: ".concat(responseMessage), - HttpURLConnection.HTTP_INTERNAL_ERROR, null, cause); - } - } + translatedException = new StorageException(request.getResult().getErrorCode(), responseMessage, + responseCode, extendedError, cause); - /** - * Translates the specified HTTP status code into a storage exception. - * - * @param statusCode - * The HTTP status code returned by the operation. - * @param statusDescription - * A String that represents the status description. - * @param inner - * An Exception object that represents a reference to the initial exception, if one exists. - * - * @return A StorageException object that represents translated exception. - **/ - protected static StorageException translateFromHttpStatus(final int statusCode, final String statusDescription, - final Exception inner) { - String errorCode; - switch (statusCode) { - case HttpURLConnection.HTTP_FORBIDDEN: - errorCode = StorageErrorCode.ACCESS_DENIED.toString(); - break; - case HttpURLConnection.HTTP_GONE: - case HttpURLConnection.HTTP_NOT_FOUND: - errorCode = StorageErrorCode.RESOURCE_NOT_FOUND.toString(); - break; - case 416: - case HttpURLConnection.HTTP_BAD_REQUEST: - // 416: RequestedRangeNotSatisfiable - No corresponding enum in HttpURLConnection - errorCode = StorageErrorCode.BAD_REQUEST.toString(); - break; - - case HttpURLConnection.HTTP_PRECON_FAILED: - case HttpURLConnection.HTTP_NOT_MODIFIED: - errorCode = StorageErrorCode.CONDITION_FAILED.toString(); - break; - - case HttpURLConnection.HTTP_CONFLICT: - errorCode = StorageErrorCode.RESOURCE_ALREADY_EXISTS.toString(); - break; - - case HttpURLConnection.HTTP_UNAVAILABLE: - errorCode = StorageErrorCode.SERVER_BUSY.toString(); - break; - - case HttpURLConnection.HTTP_GATEWAY_TIMEOUT: - errorCode = StorageErrorCode.SERVICE_TIMEOUT.toString(); - break; - - case HttpURLConnection.HTTP_INTERNAL_ERROR: - errorCode = StorageErrorCode.SERVICE_INTERNAL_ERROR.toString(); - break; - - case HttpURLConnection.HTTP_NOT_IMPLEMENTED: - errorCode = StorageErrorCode.NOT_IMPLEMENTED.toString(); - break; - - case HttpURLConnection.HTTP_BAD_GATEWAY: - errorCode = StorageErrorCode.BAD_GATEWAY.toString(); - break; - - case HttpURLConnection.HTTP_VERSION: - errorCode = StorageErrorCode.HTTP_VERSION_NOT_SUPPORTED.toString(); - break; - default: - errorCode = null; - } - - if (errorCode == null) { - return null; - } else { - return new StorageException(errorCode, statusDescription, statusCode, null, inner); - } + Utility.logHttpError(translatedException, opContext); + return translatedException; } /** diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/StorageExtendedErrorInformation.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/StorageExtendedErrorInformation.java index 7492deac269f1..7fbda4d87b31e 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/StorageExtendedErrorInformation.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/StorageExtendedErrorInformation.java @@ -61,7 +61,10 @@ public HashMap getAdditionalDetails() { * Gets the storage service error code. * * @return A String which contains the error code. + * + * @deprecated use the property on {@link RequestResult} instead. */ + @Deprecated public String getErrorCode() { return this.errorCode; } diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/BlobConstants.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/BlobConstants.java index 5f97fb7e55d70..f9baadba8a64c 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/BlobConstants.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/BlobConstants.java @@ -236,6 +236,11 @@ final class BlobConstants { */ public static final String SNAPSHOT_ELEMENT = "Snapshot"; + /** + * XML element to indicate whether blob was deleted. + */ + public static final String DELETED_ELEMENT = "Deleted"; + /** * The header for snapshots. */ @@ -266,6 +271,16 @@ final class BlobConstants { */ public static final String LEASE = "lease"; + /** + * XML element for blob properties, indicate delete time. + */ + public static final String DELETED_TIME = "DeletedTime"; + + /** + * XML element for blob properties, indicate remaining retention days. + */ + public static final String REMAINING_RETENTION_DAYS = "RemainingRetentionDays"; + /** * Private Default Constructor. */ diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/BlobListHandler.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/BlobListHandler.java index 220a2cb528d28..b8da99d66437c 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/BlobListHandler.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/BlobListHandler.java @@ -19,7 +19,6 @@ import java.net.URI; import java.net.URISyntaxException; import java.text.ParseException; -import java.util.Date; import java.util.HashMap; import java.util.Stack; @@ -53,6 +52,7 @@ final class BlobListHandler extends DefaultHandler { private CopyState copyState; private String blobName; private String snapshotID; + private boolean deleted; private BlobListHandler(CloudBlobContainer container) { this.container = container; @@ -87,6 +87,7 @@ public void startElement(String uri, String localName, String qName, Attributes this.properties = new BlobProperties(); this.metadata = new HashMap(); this.copyState = null; + this.deleted = false; } } @@ -136,6 +137,7 @@ else if (this.properties.getBlobType() == BlobType.APPEND_BLOB) { retBlob.properties = this.properties; retBlob.metadata = this.metadata; retBlob.properties.setCopyState(this.copyState); + retBlob.deleted = this.deleted; this.response.getResults().add(retBlob); } @@ -171,6 +173,9 @@ else if (BlobConstants.BLOB_ELEMENT.equals(parentNode)) { else if (BlobConstants.SNAPSHOT_ELEMENT.equals(currentNode)) { this.snapshotID = value; } + else if (BlobConstants.DELETED_ELEMENT.equals(currentNode)) { + this.deleted = Boolean.parseBoolean(value); + } } else if (BlobConstants.BLOB_PREFIX_ELEMENT.equals(parentNode)) { // Blob or BlobPrefix @@ -251,9 +256,6 @@ else if (Constants.LEASE_STATUS_ELEMENT.equals(currentNode)) { if (!tempStatus.equals(LeaseStatus.UNSPECIFIED)) { this.properties.setLeaseStatus(tempStatus); } - else { - throw new SAXException(SR.INVALID_RESPONSE_RECEIVED); - } } else if (Constants.LEASE_STATE_ELEMENT.equals(currentNode)) { final LeaseState tempState = LeaseState.parse(value); @@ -358,5 +360,11 @@ else if (Constants.ACCESS_TIER_CHANGE_TIME.equals(currentNode)) { else if (Constants.ARCHIVE_STATUS.equals(currentNode)) { this.properties.setRehydrationStatus(RehydrationStatus.parse(value)); } + else if (BlobConstants.DELETED_TIME.equals(currentNode)) { + this.properties.setDeletedTime(Utility.parseRFC1123DateFromStringInGMT(value)); + } + else if (BlobConstants.REMAINING_RETENTION_DAYS.equals(currentNode)) { + this.properties.setRemainingRetentionDays(Integer.parseInt(value)); + } } } \ No newline at end of file diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/BlobListingDetails.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/BlobListingDetails.java index 19c1971275555..a67de31a5bb41 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/BlobListingDetails.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/BlobListingDetails.java @@ -40,7 +40,12 @@ public enum BlobListingDetails { /** * Include copy properties in the listing. */ - COPY(8); + COPY(8), + + /** + * Specifies that soft deleted blobs which are retained by the service should be included in the response. + */ + DELETED(16); /** * Returns the value of this enum. diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/BlobOutputStreamInternal.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/BlobOutputStreamInternal.java index 44fde57d3f7a1..f824f7df78ca2 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/BlobOutputStreamInternal.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/BlobOutputStreamInternal.java @@ -27,13 +27,8 @@ import java.util.HashSet; import java.util.Set; import java.util.UUID; -import java.util.concurrent.Callable; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ExecutorCompletionService; -import java.util.concurrent.Future; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicInteger; import com.microsoft.azure.storage.AccessCondition; import com.microsoft.azure.storage.Constants; @@ -51,6 +46,29 @@ */ final class BlobOutputStreamInternal extends BlobOutputStream { + private static class BlobOutputStreamThreadFactory implements ThreadFactory { + private final ThreadGroup group; + private final AtomicInteger threadNumber = new AtomicInteger(1); + private final String namePrefix; + + BlobOutputStreamThreadFactory() { + SecurityManager s = System.getSecurityManager(); + group = (s != null) ? s.getThreadGroup() : + Thread.currentThread().getThreadGroup(); + namePrefix = "azure-storage-bloboutputstream-thread-"; + } + + public Thread newThread(Runnable r) { + Thread t = new Thread(group, r, + namePrefix + threadNumber.getAndIncrement(), + 0); + t.setDaemon(true); + if (t.getPriority() != Thread.NORM_PRIORITY) + t.setPriority(Thread.NORM_PRIORITY); + return t; + } + } + /** * Holds the {@link AccessCondition} object that represents the access conditions for the blob. */ @@ -171,9 +189,10 @@ private BlobOutputStreamInternal(final CloudBlob parentBlob, final AccessConditi this.threadExecutor = new ThreadPoolExecutor( this.options.getConcurrentRequestCount(), this.options.getConcurrentRequestCount(), - 10, + 10, TimeUnit.SECONDS, - new LinkedBlockingQueue()); + new LinkedBlockingQueue(), + new BlobOutputStreamThreadFactory()); this.completionService = new ExecutorCompletionService(this.threadExecutor); } @@ -473,10 +492,10 @@ private void appendBlock(ByteArrayInputStream blockData, long offset, long write if (this.options.getAbsorbConditionalErrorsOnRetry() && e.getHttpStatusCode() == HttpURLConnection.HTTP_PRECON_FAILED && e.getExtendedErrorInformation() != null - && e.getExtendedErrorInformation().getErrorCode() != null - && (e.getExtendedErrorInformation().getErrorCode() - .equals(StorageErrorCodeStrings.INVALID_APPEND_POSITION) || e.getExtendedErrorInformation() - .getErrorCode().equals(StorageErrorCodeStrings.INVALID_MAX_BLOB_SIZE_CONDITION)) + && e.getErrorCode() != null + && (e.getErrorCode() + .equals(StorageErrorCodeStrings.INVALID_APPEND_POSITION) || + e.getErrorCode().equals(StorageErrorCodeStrings.INVALID_MAX_BLOB_SIZE_CONDITION)) && (this.opContext.getRequestResults().size() - previousResultsCount > 1)) { // Pre-condition failure on a retry should be ignored in a single writer scenario since diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/BlobProperties.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/BlobProperties.java index cfc80bd229460..ceec45ec7fb79 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/BlobProperties.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/BlobProperties.java @@ -142,6 +142,16 @@ public final class BlobProperties { */ private RehydrationStatus rehydrationStatus; + /** + * Indicates when the blob was deleted. + */ + private Date deletedTime; + + /** + * If deleted, this indicates how many days the blob will be retained before it is permanently deleted. + */ + private Integer remainingRetentionDays; + /** * Creates an instance of the BlobProperties class. */ @@ -180,6 +190,8 @@ public BlobProperties(final BlobProperties other) { this.standardBlobTier = other.standardBlobTier; this.rehydrationStatus = other.rehydrationStatus; this.tierChangeTime = other.tierChangeTime; + this.deletedTime = other.deletedTime; + this.remainingRetentionDays = other.remainingRetentionDays; } /** @@ -403,6 +415,22 @@ public boolean isIncrementalCopy() { return this.isIncrementalCopy; } + /** + * Gets the time when the blob was deleted. + * @return A {@link java.util.Date} object which represents the time when the blob was deleted. It returns null if the blob has not been deleted. + */ + public Date getDeletedTime() { + return this.deletedTime; + } + + /** + * Gets the number of days that the deleted blob will be kept by the service. + * @return A Integer value that represents the number of days that the deleted blob will be kept by the service. + */ + public Integer getRemainingRetentionDays() { + return this.remainingRetentionDays; + } + /** * Sets the cache control value for the blob. * @@ -626,4 +654,22 @@ protected void setTierChangeTime(Date tierChangeTime) { protected void setRehydrationStatus(RehydrationStatus rehydrationStatus) { this.rehydrationStatus = rehydrationStatus; } + + /** + * Sets the time when the blob was deleted. + * @param deletedTime + * A {@link java.util.Date} object which represents the time when the blob was deleted. + */ + protected void setDeletedTime(Date deletedTime) { + this.deletedTime = deletedTime; + } + + /** + * Sets the number days that the deleted blob will be kept by the service. + * @param remainingRetentionDays + * A Integer value that represents the number days that the deleted blob will be kept by the service. + */ + protected void setRemainingRetentionDays(Integer remainingRetentionDays) { + this.remainingRetentionDays = remainingRetentionDays; + } } \ No newline at end of file diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/BlobRequest.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/BlobRequest.java index 8be31ddcf6aa4..3b3def4807f91 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/BlobRequest.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/BlobRequest.java @@ -54,6 +54,8 @@ final class BlobRequest { private static final String SNAPSHOTS_QUERY_ELEMENT_NAME = "snapshots"; + private static final String DELETED_QUERY_ELEMENT_NAME = "deleted"; + private static final String TIER_QUERY_ELEMENT_NAME = "tier"; private static final String UNCOMMITTED_BLOBS_QUERY_ELEMENT_NAME = "uncommittedblobs"; @@ -311,8 +313,6 @@ public static HttpURLConnection createContainer(final URI uri, final BlobRequest * * @param uri * The absolute URI to the container. - * @param timeout - * The server timeout interval. * @param query * The query builder to use. * @param blobOptions @@ -360,7 +360,8 @@ private static HttpURLConnection createURLConnection(final URI uri, final UriQue */ public static HttpURLConnection deleteBlob(final URI uri, final BlobRequestOptions blobOptions, final OperationContext opContext, final AccessCondition accessCondition, final String snapshotVersion, - final DeleteSnapshotsOption deleteSnapshotsOption) throws IOException, URISyntaxException, StorageException { + final DeleteSnapshotsOption deleteSnapshotsOption) + throws IOException, URISyntaxException, StorageException { if (snapshotVersion != null && deleteSnapshotsOption != DeleteSnapshotsOption.NONE) { throw new IllegalArgumentException(String.format(SR.DELETE_SNAPSHOT_NOT_VALID_ERROR, @@ -369,6 +370,7 @@ public static HttpURLConnection deleteBlob(final URI uri, final BlobRequestOptio final UriQueryBuilder builder = new UriQueryBuilder(); BlobRequest.addSnapshot(builder, snapshotVersion); + final HttpURLConnection request = BaseRequest.delete(uri, blobOptions, builder, opContext); if (accessCondition != null) { @@ -394,6 +396,35 @@ public static HttpURLConnection deleteBlob(final URI uri, final BlobRequestOptio return request; } + /** + * Constructs a HttpURLConnection to un-delete the blob, Sign with no length specified. + * + * @param uri + * A java.net.URI object that specifies the absolute URI. + * @param blobOptions + * A {@link BlobRequestOptions} object that specifies execution options such as retry policy and timeout + * settings for the operation. Specify null to use the request options specified on the + * {@link CloudBlobClient}. + * @param opContext + * An {@link OperationContext} object that represents the context for the current operation. This object + * is used to track requests to the storage service, and to provide additional runtime information about + * the operation. + * @return a HttpURLConnection to use to perform the operation. + * @throws IOException + * if there is an error opening the connection + * @throws URISyntaxException + * if the resource URI is invalid + * @throws StorageException + * an exception representing any error which occurred during the operation. + * @throws IllegalArgumentException + */ + public static HttpURLConnection undeleteBlob(final URI uri, final BlobRequestOptions blobOptions, + final OperationContext opContext) throws IOException, URISyntaxException, StorageException { + + final UriQueryBuilder builder = new UriQueryBuilder(); + return BaseRequest.undelete(uri, blobOptions, builder, opContext); + } + /** * Constructs a web request to delete the container and all of blobs within it. Sign with no length specified. * @@ -430,8 +461,6 @@ public static HttpURLConnection deleteContainer(final URI uri, final BlobRequest * * @param uri * The absolute URI to the container. - * @param timeout - * The server timeout interval. * @param accessCondition * An {@link AccessCondition} object that represents the access conditions for the container. * @param opContext @@ -828,9 +857,6 @@ private static HttpURLConnection getProperties(final URI uri, final BlobRequestO * @param breakPeriodInSeconds * Specifies the amount of time to allow the lease to remain, in seconds. * If null, the break period is the remainder of the current lease, or zero for infinite leases. - * @param visibilityTimeoutInSeconds - * Specifies the the span of time for which to acquire the lease, in seconds. - * If null, an infinite lease will be acquired. If not null, this must be greater than zero. * @return a HttpURLConnection to use to perform the operation. * @throws IOException * if there is an error opening the connection @@ -903,9 +929,6 @@ private static HttpURLConnection lease(final URI uri, final BlobRequestOptions b * @param breakPeriodInSeconds * Specifies the amount of time to allow the lease to remain, in seconds. * If null, the break period is the remainder of the current lease, or zero for infinite leases. - * @param visibilityTimeoutInSeconds - * Specifies the the span of time for which to acquire the lease, in seconds. - * If null, an infinite lease will be acquired. If not null, this must be greater than zero. * @return a HttpURLConnection to use to perform the operation. * @throws IOException * if there is an error opening the connection @@ -949,9 +972,6 @@ public static HttpURLConnection leaseBlob(final URI uri, final BlobRequestOption * @param breakPeriodInSeconds * Specifies the amount of time to allow the lease to remain, in seconds. * If null, the break period is the remainder of the current lease, or zero for infinite leases. - * @param visibilityTimeoutInSeconds - * Specifies the the span of time for which to acquire the lease, in seconds. - * If null, an infinite lease will be acquired. If not null, this must be greater than zero. * @return a HttpURLConnection to use to perform the operation. * @throws IOException * if there is an error opening the connection @@ -1070,6 +1090,17 @@ public static HttpURLConnection listBlobs(final URI uri, final BlobRequestOption sb.append(Constants.QueryConstants.METADATA); } + if (listingContext.getListingDetails().contains(BlobListingDetails.DELETED)) { + if (!started) { + started = true; + } + else { + sb.append(","); + } + + sb.append(DELETED_QUERY_ELEMENT_NAME); + } + builder.add(Constants.QueryConstants.INCLUDE, sb.toString()); } } diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/CloudBlob.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/CloudBlob.java index fb7ff88af8305..8aa9a157c4422 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/CloudBlob.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/CloudBlob.java @@ -14,7 +14,6 @@ */ package com.microsoft.azure.storage.blob; -import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.File; @@ -84,6 +83,11 @@ public abstract class CloudBlob implements ListBlobItem { */ String snapshotID; + /** + * Indicates whether the blob is being retained by the service after being deleted. + */ + boolean deleted; + /** * Holds the blob's container reference. */ @@ -922,6 +926,9 @@ else if (blob instanceof CloudAppendBlob) { /** * Deletes the blob. * + * If a delete retention policy is enabled on the service, the blob will be retained for a specified period of time, + * before being removed permanently by garbage collection. + * * @throws StorageException * If a storage service error occurred. */ @@ -932,6 +939,10 @@ public final void delete() throws StorageException { /** * Deletes the blob using the specified snapshot and request options, and operation context. + * + * If a delete retention policy is enabled on the service, the blob will be retained for a specified period of time, + * before being removed permanently by garbage collection. + * *

* A blob that has snapshots cannot be deleted unless the snapshots are also deleted. If a blob has snapshots, use * the {@link DeleteSnapshotsOption#DELETE_SNAPSHOTS_ONLY} or {@link DeleteSnapshotsOption#INCLUDE_SNAPSHOTS} value @@ -972,6 +983,49 @@ public final void delete(final DeleteSnapshotsOption deleteSnapshotsOption, fina opContext); } + /** + * Un-deletes a blob and all its snapshots that have been soft-deleted. + * + * @throws StorageException + * If a storage service error occurred. + */ + @DoesServiceRequest + public final void undelete() throws StorageException { + this.undelete(null /* options */, null /* opContext */); + } + + /** + * Un-deletes a blob that has been soft-deleted, using the specified request options, and operation context. + *

+ * The un-delete Blob operation restores the contents and metadata of soft deleted blob and all its snapshots. + * Attempting to undelete a blob or snapshot that is not soft deleted will succeed without any changes. + * + * @param options + * A {@link BlobRequestOptions} object that specifies any additional options for the request. Specifying + * null will use the default request options from the associated service client ( + * {@link CloudBlobClient}). + * @param opContext + * An {@link OperationContext} object that represents the context for the current operation. This object + * is used to track requests to the storage service, and to provide additional runtime information about + * the operation. + * + * @throws StorageException + * If a storage service error occurred. + */ + @DoesServiceRequest + public final void undelete(BlobRequestOptions options, OperationContext opContext) throws StorageException { + if (opContext == null) { + opContext = new OperationContext(); + } + + opContext.initialize(); + options = BlobRequestOptions.populateAndApplyDefaults(options, this.properties.getBlobType(), this.blobServiceClient); + + ExecutionEngine.executeWithRetry(this.blobServiceClient, this, + this.undeleteImpl(options), options.getRetryPolicyFactory(), + opContext); + } + /** * Deletes the blob if it exists. *

@@ -1076,11 +1130,59 @@ public Void preProcessResponse(CloudBlob parentObject, CloudBlobClient client, O return null; } + + @Override + public Void postProcessResponse(HttpURLConnection connection, CloudBlob parentObject, CloudBlobClient client, OperationContext context, Void storageObject) throws Exception { + if (this.getResult().getStatusCode() == HttpURLConnection.HTTP_ACCEPTED) { + parentObject.deleted = true; + } + return null; + } }; return deleteRequest; } + private StorageRequest undeleteImpl(final BlobRequestOptions options) { + final StorageRequest undeleteRequest = new StorageRequest( + options, this.getStorageUri()) { + + @Override + public HttpURLConnection buildRequest(CloudBlobClient client, CloudBlob blob, OperationContext context) + throws Exception { + return BlobRequest.undeleteBlob(blob.getTransformedAddress(context).getUri(this.getCurrentLocation()), + options, context); + } + + @Override + public void signRequest(HttpURLConnection connection, CloudBlobClient client, OperationContext context) + throws Exception { + StorageRequest.signBlobQueueAndFileRequest(connection, client, -1L, context); + } + + @Override + public Void preProcessResponse(CloudBlob parentObject, CloudBlobClient client, OperationContext context) + throws Exception { + if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_OK) { + this.setNonExceptionedRetryableFailure(true); + return null; + } + + return null; + } + + @Override + public Void postProcessResponse(HttpURLConnection connection, CloudBlob parentObject, CloudBlobClient client, OperationContext context, Void storageObject) throws Exception { + if (this.getResult().getStatusCode() == HttpURLConnection.HTTP_OK) { + parentObject.deleted = false; + } + return null; + } + }; + + return undeleteRequest; + } + /** * Downloads the contents of a blob to a stream. * @@ -1765,7 +1867,7 @@ public void uploadFromFile(final String path, final AccessCondition accessCondit OperationContext opContext) throws StorageException, IOException { File file = new File(path); long fileLength = file.length(); - InputStream inputStream = new BufferedInputStream(new FileInputStream(file)); + InputStream inputStream = new FileInputStream(file); // The call to upload supports FileInputStream efficiently. this.upload(inputStream, fileLength, accessCondition, options, opContext); inputStream.close(); } @@ -2375,8 +2477,6 @@ public final BlobInputStream openInputStream(final AccessCondition accessConditi opContext = new OperationContext(); } - assertNoWriteOperationForSnapshot(); - options = BlobRequestOptions.populateAndApplyDefaults(options, this.properties.getBlobType(), this.blobServiceClient, false /* setStartTime */); diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/CloudBlobClient.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/CloudBlobClient.java index 1df73f89e86eb..0994ce3ad6059 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/CloudBlobClient.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/CloudBlobClient.java @@ -560,6 +560,10 @@ public void uploadServiceProperties(final ServiceProperties properties, BlobRequ options = BlobRequestOptions.populateAndApplyDefaults(options, BlobType.UNSPECIFIED, this); Utility.assertNotNull("properties", properties); + if (properties.getDeleteRetentionPolicy() != null + && properties.getDeleteRetentionPolicy().getEnabled()) { + Utility.assertNotNull("RetentionIntervalInDays", properties.getDeleteRetentionPolicy().getRetentionIntervalInDays()); + } ExecutionEngine.executeWithRetry(this, null, this.uploadServicePropertiesImpl(properties, options, opContext, false), diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/CloudBlobContainer.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/CloudBlobContainer.java index 9687a9fa5210c..089debd090425 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/CloudBlobContainer.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/CloudBlobContainer.java @@ -1294,7 +1294,7 @@ public Iterable listBlobs(final String prefix, final boolean useFl * the returned list will be hierarchical. * @param listingDetails * A java.util.EnumSet object that contains {@link BlobListingDetails} values that indicate - * whether snapshots, metadata, and/or uncommitted blocks are returned. Committed blocks are always + * whether snapshots, soft-deleted blobs, metadata, and/or uncommitted blocks are returned. Committed blocks are always * returned. * @param options * A {@link BlobRequestOptions} object that specifies any additional options for the request. Specifying @@ -1372,7 +1372,7 @@ public ResultSegment listBlobsSegmented(final String prefix) throw * false to indicate that the returned list will be hierarchical. * @param listingDetails * A java.util.EnumSet object that contains {@link BlobListingDetails} - * values that indicate whether snapshots, metadata, and/or uncommitted blocks + * values that indicate whether snapshots, soft-deleted blobs, metadata, and/or uncommitted blocks * are returned. Committed blocks are always returned. * @param maxResults * The maximum number of results to retrieve. If null or greater diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/CloudBlockBlob.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/CloudBlockBlob.java index 94e70ebab9599..2e03484e6417e 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/CloudBlockBlob.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/blob/CloudBlockBlob.java @@ -25,6 +25,7 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; +import java.io.FileInputStream; import java.net.HttpURLConnection; import java.net.URI; import java.net.URISyntaxException; @@ -644,8 +645,16 @@ public void upload(final InputStream sourceStream, final long length, final Acce StreamMd5AndLength descriptor = new StreamMd5AndLength(); descriptor.setLength(length); - - InputStream inputDataStream = sourceStream; + + // If the sourceStream is a FileInputStream, wrap it in a MarkableFileStream. + // This allows for single shot upload on FileInputStreams. + InputStream inputDataStream; + if(!sourceStream.markSupported() && sourceStream instanceof FileInputStream) { + inputDataStream = new MarkableFileStream((FileInputStream)sourceStream); + } + else { + inputDataStream = sourceStream; + } // Initial check - skip the PutBlob operation if the input stream isn't markable, or if the length is known to // be greater than the threshold. @@ -821,7 +830,7 @@ private void uploadFromMultiStream(Iterable streamList, AccessCondi final InputStream sourceStream = block; final long blockSize = block instanceof SubStream ? ((SubStream) block).getLength() : this.streamWriteSizeInBytes; - Future uploadTask = completionService.submit(new Callable() { + completionService.submit(new Callable() { @Override public Void call() throws IOException, StorageException { uploadBlock(blockId, sourceStream, blockSize, _accessCondition, _requestOptions, _operationContext); @@ -1018,19 +1027,29 @@ public void uploadBlock(final String blockId, final InputStream sourceStream, fi throw new IllegalArgumentException(SR.INVALID_BLOCK_ID); } - if (sourceStream.markSupported()) { + // If the sourceStream is a FileInputStream, wrap it in a MarkableFileStream. + // This prevents buffering the entire block into memory. + InputStream bufferedStreamReference; + if(!sourceStream.markSupported() && sourceStream instanceof FileInputStream) { + bufferedStreamReference = new MarkableFileStream((FileInputStream)sourceStream); + } + else { + bufferedStreamReference = sourceStream; + } + + if (bufferedStreamReference.markSupported()) { // Mark sourceStream for current position. - sourceStream.mark(Constants.MAX_MARK_LENGTH); + bufferedStreamReference.mark(Constants.MAX_MARK_LENGTH); } - InputStream bufferedStreamReference = sourceStream; StreamMd5AndLength descriptor = new StreamMd5AndLength(); descriptor.setLength(length); - if (!sourceStream.markSupported()) { + if (!bufferedStreamReference.markSupported()) { // needs buffering + // TODO: Change to a BufferedInputStream to avoid the extra buffering and copying. final ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); - descriptor = Utility.writeToOutputStream(sourceStream, byteStream, length, false /* rewindSourceStream */, + descriptor = Utility.writeToOutputStream(bufferedStreamReference, byteStream, length, false /* rewindSourceStream */, options.getUseTransactionalContentMD5(), opContext, options); bufferedStreamReference = new ByteArrayInputStream(byteStream.toByteArray()); @@ -1038,7 +1057,7 @@ public void uploadBlock(final String blockId, final InputStream sourceStream, fi else if (length < 0 || options.getUseTransactionalContentMD5()) { // If the stream is of unknown length or we need to calculate the // MD5, then we we need to read the stream contents first - descriptor = Utility.analyzeStream(sourceStream, length, -1L, true /* rewindSourceStream */, + descriptor = Utility.analyzeStream(bufferedStreamReference, length, -1L, true /* rewindSourceStream */, options.getUseTransactionalContentMD5()); } diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/core/BaseRequest.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/core/BaseRequest.java index 1d06cc6abb5d5..ec0e317335010 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/core/BaseRequest.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/core/BaseRequest.java @@ -218,8 +218,6 @@ public static HttpURLConnection createURLConnection(final URI uri, final Request * * @param uri * the request Uri. - * @param timeout - * the timeout for the request * @param builder * the UriQueryBuilder for the request * @param opContext @@ -243,6 +241,36 @@ public static HttpURLConnection delete(final URI uri, final RequestOptions optio return retConnection; } + /** + * Un-deletes the specified resource. Sign with no length specified. + * + * @param uri + * the request Uri. + * @param builder + * the UriQueryBuilder for the request + * @param opContext + * an object used to track the execution of the operation + * @return a HttpURLConnection to perform the operation. + * @throws IOException + * if there is an error opening the connection + * @throws URISyntaxException + * if there is an improperly formated URI + * @throws StorageException + */ + public static HttpURLConnection undelete(final URI uri, final RequestOptions options, UriQueryBuilder builder, + final OperationContext opContext) throws IOException, URISyntaxException, StorageException { + if (builder == null) { + builder = new UriQueryBuilder(); + } + builder.add(Constants.QueryConstants.COMPONENT, Constants.QueryConstants.UNDELETE); + + final HttpURLConnection retConnection = createURLConnection(uri, options, builder, opContext); + retConnection.setFixedLengthStreamingMode(0); + retConnection.setDoOutput(true); + retConnection.setRequestMethod(Constants.HTTP_PUT); + return retConnection; + } + /** * Gets a {@link UriQueryBuilder} for listing. * @@ -279,8 +307,6 @@ public static UriQueryBuilder getListUriQueryBuilder(final ListingContext listin * * @param uri * The Uri to query. - * @param timeout - * The timeout. * @param builder * The builder. * @param opContext @@ -307,8 +333,6 @@ public static HttpURLConnection getProperties(final URI uri, final RequestOption * * @param uri * The service endpoint. - * @param timeout - * The timeout. * @param builder * The builder. * @param opContext @@ -339,8 +363,6 @@ public static HttpURLConnection getServiceProperties(final URI uri, final Reques * * @param uri * The service endpoint. - * @param timeout - * The timeout. * @param builder * The builder. * @param opContext @@ -388,8 +410,6 @@ public static String getUserAgent() { * * @param uri * The blob Uri. - * @param timeout - * The timeout. * @param builder * The builder. * @param opContext @@ -421,8 +441,6 @@ public static HttpURLConnection setMetadata(final URI uri, final RequestOptions * * @param uri * The service endpoint. - * @param timeout - * The timeout. * @param builder * The builder. * @param opContext diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/core/BaseResponse.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/core/BaseResponse.java index 68b75b0d8b91f..c6f250ca0796a 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/core/BaseResponse.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/core/BaseResponse.java @@ -61,7 +61,21 @@ public static String getEtag(final HttpURLConnection request) { } /** - * Gets the metadata from the request The response from server. + * Gets the ErrorCode + * + * @param request + * The response from server. + * @return The ErrorCode. + */ + public static String getErrorCode(final HttpURLConnection request) { + return request.getHeaderField(Constants.HeaderConstants.ERROR_CODE); + } + + /** + * Gets the metadata from the request. + * + * @param request + * The response from server. * * @return the metadata from the request */ diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/core/ExecutionEngine.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/core/ExecutionEngine.java index 06cf8c8e03baf..8f6d5e2b33e7a 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/core/ExecutionEngine.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/core/ExecutionEngine.java @@ -87,6 +87,15 @@ public static RESULT_TYPE executeWithRet try { if (task.getSendStream() != null) { Logger.info(opContext, LogConstants.UPLOAD); + + // Always set fixed length streaming mode when we know the length in advance. + // This sets the Content-Length of the request and allows the request payload to + // be streamed from the source. Otherwise, the HTTP layer will buffer the entire + // request and compute the Content-Length itself. + if (task.getLength() >= 0) { + request.setFixedLengthStreamingMode(task.getLength()); + } + final StreamMd5AndLength descriptor = Utility.writeToOutputStream(task.getSendStream(), request.getOutputStream(), task.getLength(), false /* rewindStream */, false /* calculate MD5 */, opContext, task.getRequestOptions()); @@ -111,6 +120,7 @@ public static RESULT_TYPE executeWithRet currResult.setEtag(BaseResponse.getEtag(request)); currResult.setRequestDate(BaseResponse.getDate(request)); currResult.setContentMD5(BaseResponse.getContentMD5(request)); + currResult.setErrorCode(BaseResponse.getErrorCode(request)); // 7. Fire ResponseReceived Event responseReceivedEventTriggered = true; diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/core/MarkableFileStream.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/core/MarkableFileStream.java new file mode 100644 index 0000000000000..a31be9cc68f90 --- /dev/null +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/core/MarkableFileStream.java @@ -0,0 +1,57 @@ +/** + * 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.core; + +import java.io.FileInputStream; +import java.io.FilterInputStream; +import java.io.IOException; +import java.nio.channels.FileChannel; + +/** + * RESERVED FOR INTERNAL USE. Wraps a FileStream to allow for more memory efficient uploading. + */ +public final class MarkableFileStream extends FilterInputStream { + private long mark = -1; + private FileChannel fileChannel; + + public MarkableFileStream(FileInputStream stream) { + super(stream); + this.fileChannel = stream.getChannel(); + } + + @Override + public synchronized void mark(int readlimit) { + try { + this.mark = this.fileChannel.position(); + } + catch (IOException e) { + this.mark = -1; + } + } + + @Override + public synchronized void reset() throws IOException { + if(this.mark == -1){ + throw new IOException("Stream must be marked before calling reset"); + } + + this.fileChannel.position(this.mark); + } + + @Override + public boolean markSupported() { + return true; + } +} diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/core/SR.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/core/SR.java index 086d6e6dccae7..db77c05d5fbf7 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/core/SR.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/core/SR.java @@ -22,6 +22,7 @@ public class SR { public static final String ACCOUNT_NAME_NULL_OR_EMPTY = "The account name is null or empty."; public static final String ACCOUNT_NAME_MISMATCH = "The account name does not match the existing account name on the credentials."; public static final String APPEND_BLOB_MD5_NOT_POSSIBLE = "MD5 cannot be calculated for an existing append blob because it would require reading the existing data. Please disable StoreFileContentMD5."; + public static final String ARGUMENT_NULL = "The argument must not be null. Argument name: %s."; public static final String ARGUMENT_NULL_OR_EMPTY = "The argument must not be null or an empty string. Argument name: %s."; public static final String ARGUMENT_OUT_OF_RANGE_ERROR = "The argument is out of range. Argument name: %s, Value passed: %s."; public static final String ATTEMPTED_TO_SERIALIZE_INACCESSIBLE_PROPERTY = "An attempt was made to access an inaccessible member of the entity during serialization."; diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/core/Utility.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/core/Utility.java index 8facaed5fc900..ca7bf44110ddf 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/core/Utility.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/core/Utility.java @@ -365,7 +365,7 @@ public static void assertContinuationType(final ResultContinuation continuationT */ public static void assertNotNull(final String param, final Object value) { if (value == null) { - throw new IllegalArgumentException(String.format(Utility.LOCALE_US, SR.ARGUMENT_NULL_OR_EMPTY, param)); + throw new IllegalArgumentException(String.format(Utility.LOCALE_US, SR.ARGUMENT_NULL, param)); } } diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/queue/CloudQueue.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/queue/CloudQueue.java index 114adae9c1cba..6eee44a10a305 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/queue/CloudQueue.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/queue/CloudQueue.java @@ -224,12 +224,12 @@ public void addMessage(final CloudQueueMessage message) throws StorageException * * @param timeToLiveInSeconds * The maximum time to allow the message to be in the queue. A value of zero will set the time-to-live to - * the service default value of seven days. + * the service default value of seven days. A value of negative one will set an infinite time-to-live. * * @param initialVisibilityDelayInSeconds * The length of time during which the message will be invisible, starting when it is added to the queue, * or 0 to make the message visible immediately. This value must be greater than or equal to zero and - * less than or equal to the time-to-live value. + * less than the time-to-live value. * * @param options * A {@link QueueRequestOptions} object that specifies any additional options for the request. Specifying @@ -250,13 +250,19 @@ public void addMessage(final CloudQueueMessage message, final int timeToLiveInSe throws StorageException { Utility.assertNotNull("message", message); Utility.assertNotNull("messageContent", message.getMessageContentAsByte()); - Utility.assertInBounds("timeToLiveInSeconds", timeToLiveInSeconds, 0, - QueueConstants.MAX_TIME_TO_LIVE_IN_SECONDS); - final int realTimeToLiveInSeconds = timeToLiveInSeconds == 0 ? QueueConstants.MAX_TIME_TO_LIVE_IN_SECONDS - : timeToLiveInSeconds; + if (!(timeToLiveInSeconds == -1 || timeToLiveInSeconds >= 0)){ + throw new IllegalArgumentException(String.format(SR.ARGUMENT_OUT_OF_RANGE_ERROR, + "timeToLiveInSeconds", timeToLiveInSeconds)); + } + // If ttl is 0, it will default to 7 days (MAX_VISIBILITY_TIMOUT) on the service + int realTimeToLiveInSeconds = timeToLiveInSeconds == 0 ? QueueConstants.MAX_VISIBILITY_TIMEOUT_IN_SECONDS : timeToLiveInSeconds; + + // Ensures the visibilityTimeout is less than or equal to the max allowed and strictly less than the TTL. + int visibilityUpperBound = ((realTimeToLiveInSeconds < 0) || realTimeToLiveInSeconds - 1 > QueueConstants.MAX_VISIBILITY_TIMEOUT_IN_SECONDS) ? + QueueConstants.MAX_VISIBILITY_TIMEOUT_IN_SECONDS : realTimeToLiveInSeconds - 1; Utility.assertInBounds("initialVisibilityDelayInSeconds", initialVisibilityDelayInSeconds, 0, - realTimeToLiveInSeconds - 1); + visibilityUpperBound); if (opContext == null) { opContext = new OperationContext(); @@ -1262,7 +1268,7 @@ public Iterable retrieveMessages(final int numberOfMessages, throws StorageException { Utility.assertInBounds("numberOfMessages", numberOfMessages, 1, QueueConstants.MAX_NUMBER_OF_MESSAGES_TO_PEEK); Utility.assertInBounds("visibilityTimeoutInSeconds", visibilityTimeoutInSeconds, 0, - QueueConstants.MAX_TIME_TO_LIVE_IN_SECONDS); + QueueConstants.MAX_VISIBILITY_TIMEOUT_IN_SECONDS); if (opContext == null) { opContext = new OperationContext(); @@ -1400,7 +1406,7 @@ public void updateMessage(final CloudQueueMessage message, final int visibilityT Utility.assertNotNullOrEmpty("popReceipt", message.getPopReceipt()); Utility.assertInBounds("visibilityTimeoutInSeconds", visibilityTimeoutInSeconds, 0, - QueueConstants.MAX_TIME_TO_LIVE_IN_SECONDS); + QueueConstants.MAX_VISIBILITY_TIMEOUT_IN_SECONDS); if (opContext == null) { opContext = new OperationContext(); diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/queue/CloudQueueMessage.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/queue/CloudQueueMessage.java index e2c6e6f8617be..7aa6e530900ed 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/queue/CloudQueueMessage.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/queue/CloudQueueMessage.java @@ -293,7 +293,7 @@ protected final void setExpirationTime(final Date expirationTime) { /** * Sets the java.util.Date representing the time the message was added to the queue. * - * @param insertiontTime + * @param insertionTime * The java.util.Date representing the time the message was added to the queue. */ protected final void setInsertionTime(Date insertionTime) { diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/queue/QueueConstants.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/queue/QueueConstants.java index 3705b267f42a2..852572042ad27 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/queue/QueueConstants.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/queue/QueueConstants.java @@ -58,7 +58,7 @@ final class QueueConstants { /** * The maximum amount of time a message is kept inside the queue, in seconds. */ - public static final int MAX_TIME_TO_LIVE_IN_SECONDS = 7 * 24 * 60 * 60; + public static final int MAX_VISIBILITY_TIMEOUT_IN_SECONDS = 7 * 24 * 60 * 60; /** * XML element for MessageId. diff --git a/pom.xml b/pom.xml index 9fb89b9445d0f..16660f6228f1e 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ 4.0.0 com.microsoft.azure azure-storage - 6.1.0 + 7.0.0 jar Microsoft Azure Storage Client SDK @@ -73,12 +73,12 @@ com.microsoft.azure azure-keyvault-core - 0.8.0 + 1.0.0 com.microsoft.azure azure-keyvault-extensions - 0.8.0 + 1.0.0 test