From d87dd4b3f79a8072bc7902960810d8fc3d19ac65 Mon Sep 17 00:00:00 2001 From: "Keith Farmer (MSFT)" Date: Fri, 1 Sep 2017 13:05:07 -0700 Subject: [PATCH 01/10] Port .NET connection string work --- BreakingChanges.txt | 6 +- ChangeLog.txt | 4 + .../azure/storage/StorageAccountTests.java | 421 +++++++++++++++++- .../azure/storage/CloudStorageAccount.java | 374 +++++++++++++--- .../azure/storage/StorageCredentials.java | 2 +- 5 files changed, 726 insertions(+), 81 deletions(-) diff --git a/BreakingChanges.txt b/BreakingChanges.txt index d3cb92e4f0e6a..8daeffb1454bc 100644 --- a/BreakingChanges.txt +++ b/BreakingChanges.txt @@ -1,4 +1,8 @@ -Changes in 5.1.1 +Changes in X.X.X + * DefaultEndpointsProtocol will now be explicitly included in generated connection strings. + * Connection string parsing has been substantially re-written and expanded. Please refer to current documentation about connection string formats. + +Changes in 5.1.1 OTHER * Reverted the code from 5.1.0 because it contained a regression and an accidental change. diff --git a/ChangeLog.txt b/ChangeLog.txt index f0ce159c60467..0d5b844f8df0e 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -1,3 +1,7 @@ +X.X.X + * Connection string support expanded to allow AccountName to be specified with SharedAccessSignature and DefaultEndpointsProtocol. In this case, SharedAccessSignature is used for credentials, while having both DefaultEndpointsProtocol and AccountName allows the library to infer a set of default endpoints. Additionally, we have added support for BlobSecondaryEndpoint, QueueSecondaryEndpoint, TableSecondaryEndpoint, and FileSecondaryEndpoint. Specifying a secondary endpoint requires the specification of the corresponding primary. + * All: The use of DefaultEndpointsProtocol in a connection string is now optional in the case where endpoints would be automatically generated; if missing, a value of https will be inferred. When the parsed account settings in such a case are used to generate a connection string, the value of DefaultEndpointsProtocol will be explicitly included. + 2017.08.28 Version 5.5.0 * Fixed a bug when using a SAS token where the token was being omitted from calls to delete a directory in the file service. * For Standard Storage Accounts only, added the ability to set the tier of individual block blobs. The tier can currently only be set through uploadTier() diff --git a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/StorageAccountTests.java b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/StorageAccountTests.java index 33281527309fe..382a91f5d36ab 100644 --- a/microsoft-azure-storage-test/src/com/microsoft/azure/storage/StorageAccountTests.java +++ b/microsoft-azure-storage-test/src/com/microsoft/azure/storage/StorageAccountTests.java @@ -282,21 +282,406 @@ public void testCloudStorageAccountDefaultStorageAccountWithHttps() throws URISy @Test public void testCloudStorageAccountConnectionStringRoundtrip() throws InvalidKeyException, URISyntaxException { - String accountString1 = String.format("DefaultEndpointsProtocol=http;AccountName=%s;AccountKey=%s", - ACCOUNT_NAME, ACCOUNT_KEY); - - String accountString2 = String.format( - "DefaultEndpointsProtocol=https;AccountName=%s;AccountKey=%s;QueueEndpoint=%s", ACCOUNT_NAME, - ACCOUNT_KEY, "https://alternate.queue.endpoint/"); + Object[] accountKeyParams = new String[] + { + ACCOUNT_NAME, + ACCOUNT_KEY, + "fake.endpoint.suffix", + "https://primary.endpoint/", + "https://secondary.endpoint/" + }; + + Object[] accountSasParams = new String[] + { + ACCOUNT_NAME, + "sasTest", + "fake.endpoint.suffix", + "https://primary.endpoint/", + "https://secondary.endpoint/" + }; + + // account key + + String accountString1 = + String.format( + "DefaultEndpointsProtocol=http;AccountName=%1$s;AccountKey=%2$s;EndpointSuffix=%3$s;", + accountKeyParams); + + String accountString2 = + String.format( + "DefaultEndpointsProtocol=https;AccountName=%1$s;AccountKey=%2$s;", + accountKeyParams); + + String accountString3 = + String.format( + "DefaultEndpointsProtocol=https;AccountName=%1$s;AccountKey=%2$s;QueueEndpoint=%4$s", + accountKeyParams); + + String accountString4 = + String.format( + "DefaultEndpointsProtocol=https;AccountName=%1$s;AccountKey=%2$s;EndpointSuffix=%3$s;QueueEndpoint=%4$s", + accountKeyParams); connectionStringRoundtripHelper(accountString1); connectionStringRoundtripHelper(accountString2); + connectionStringRoundtripHelper(accountString3); + connectionStringRoundtripHelper(accountString4); + + String accountString5 = + String.format( + "AccountName=%1$s;AccountKey=%2$s;EndpointSuffix=%3$s;", + accountKeyParams); + + String accountString6 = + String.format( + "AccountName=%1$s;AccountKey=%2$s;", + accountKeyParams); + + String accountString7 = + String.format( + "AccountName=%1$s;AccountKey=%2$s;QueueEndpoint=%4$s", + accountKeyParams); + + String accountString8 = + String.format( + "AccountName=%1$s;AccountKey=%2$s;EndpointSuffix=%3$s;QueueEndpoint=%4$s", + accountKeyParams); + + connectionStringRoundtripHelper(accountString5); + connectionStringRoundtripHelper(accountString6); + connectionStringRoundtripHelper(accountString7); + connectionStringRoundtripHelper(accountString8); + + // shared access + + String accountString9 = + String.format( + "DefaultEndpointsProtocol=http;AccountName=%1$s;SharedAccessSignature=%2$s;EndpointSuffix=%3$s;", + accountSasParams); + + String accountString10 = + String.format( + "DefaultEndpointsProtocol=https;AccountName=%1$s;SharedAccessSignature=%2$s;", + accountSasParams); + + String accountString11 = + String.format( + "DefaultEndpointsProtocol=https;AccountName=%1$s;SharedAccessSignature=%2$s;QueueEndpoint=%4$s", + accountSasParams); + + String accountString12 = + String.format( + "DefaultEndpointsProtocol=https;AccountName=%1$s;SharedAccessSignature=%2$s;EndpointSuffix=%3$s;QueueEndpoint=%4$s", + accountSasParams); + + connectionStringRoundtripHelper(accountString9); + connectionStringRoundtripHelper(accountString10); + connectionStringRoundtripHelper(accountString11); + connectionStringRoundtripHelper(accountString12); + + String accountString13 = + String.format( + "AccountName=%1$s;SharedAccessSignature=%2$s;EndpointSuffix=%3$s;", + accountSasParams); + + String accountString14 = + String.format( + "AccountName=%1$s;SharedAccessSignature=%2$s;", + accountSasParams); + + String accountString15 = + String.format( + "AccountName=%1$s;SharedAccessSignature=%2$s;QueueEndpoint=%4$s", + accountSasParams); + + String accountString16 = + String.format( + "AccountName=%1$s;SharedAccessSignature=%2$s;EndpointSuffix=%3$s;QueueEndpoint=%4$s", + accountSasParams); + + connectionStringRoundtripHelper(accountString13); + connectionStringRoundtripHelper(accountString14); + connectionStringRoundtripHelper(accountString15); + connectionStringRoundtripHelper(accountString16); + + // shared access no account name + + String accountString17 = + String.format( + "SharedAccessSignature=%2$s;QueueEndpoint=%4$s", + accountSasParams); + + connectionStringRoundtripHelper(accountString17); + } + + @Test + public void CloudStorageAccountConnectionStringExpectedExceptions() + { + String[][] endpointCombinations = new String[][] + { + new String[] { "BlobEndpoint=%4$s", "BlobSecondaryEndpoint=%5$s", "BlobEndpoint=%4$s;BlobSecondaryEndpoint=%5$s" }, + new String[] { "QueueEndpoint=%4$s", "QueueSecondaryEndpoint=%5$s", "QueueEndpoint=%4$s;QueueSecondaryEndpoint=%5$s" }, + new String[] { "TableEndpoint=%4$s", "TableSecondaryEndpoint=%5$s", "TableEndpoint=%4$s;TableSecondaryEndpoint=%5$s" }, + new String[] { "FileEndpoint=%4$s", "FileSecondaryEndpoint=%5$s", "FileEndpoint=%4$s;FileSecondaryEndpoint=%5$s" } + }; + + Object[] accountKeyParams = new String[] + { + ACCOUNT_NAME, + ACCOUNT_KEY, + "fake.endpoint.suffix", + "https://primary.endpoint/", + "https://secondary.endpoint/" + }; + + Object[] accountSasParams = new String[] + { + ACCOUNT_NAME, + "sasTest", + "fake.endpoint.suffix", + "https://primary.endpoint/", + "https://secondary.endpoint/" + }; + + for (String[] endpointCombination: endpointCombinations) + { + // account key + + String accountStringKeyPrimary = + String.format( + "DefaultEndpointsProtocol=https;AccountName=%1$s;AccountKey=%2$s;EndpointSuffix=%3$s;" + endpointCombination[0], + accountKeyParams + ); + + String accountStringKeySecondary = + String.format( + "DefaultEndpointsProtocol=https;AccountName=%1$s;AccountKey=%2$s;EndpointSuffix=%3$s;" + endpointCombination[1], + accountKeyParams + ); + + String accountStringKeyPrimarySecondary = + String.format( + "DefaultEndpointsProtocol=https;AccountName=%1$s;AccountKey=%2$s;EndpointSuffix=%3$s;" + endpointCombination[2], + accountKeyParams + ); + + try { + CloudStorageAccount.parse(accountStringKeyPrimary); // no exception expected + } catch (Exception e) { + fail("Unexpected exception"); + } + + try { + CloudStorageAccount.parse(accountStringKeySecondary); + fail("Expected exception not thrown"); + } catch (IllegalArgumentException e) { + // pass + } catch (InvalidKeyException e) { + fail("Unexpected exception"); + } catch (URISyntaxException e) { + fail("Unexpected exception"); + } + + try { + CloudStorageAccount.parse(accountStringKeyPrimarySecondary); // no exception expected + } catch (Exception e) { + fail("Unexpected exception"); + } + + // account key, no default protocol + + String accountStringKeyNoDefaultProtocolPrimary = + String.format( + "AccountName=%1$s;AccountKey=%2$s;EndpointSuffix=%3$s;" + endpointCombination[0], + accountKeyParams + ); + + String accountStringKeyNoDefaultProtocolSecondary = + String.format( + "AccountName=%1$s;AccountKey=%2$s;EndpointSuffix=%3$s;" + endpointCombination[1], + accountKeyParams + ); + + String accountStringKeyNoDefaultProtocolPrimarySecondary = + String.format( + "AccountName=%1$s;AccountKey=%2$s;EndpointSuffix=%3$s;" + endpointCombination[2], + accountKeyParams + ); + + try { + CloudStorageAccount.parse(accountStringKeyNoDefaultProtocolPrimary); // no exception expected + } catch (Exception e) { + fail("Unexpected exception"); + } + + try { + CloudStorageAccount.parse(accountStringKeyNoDefaultProtocolSecondary); + fail("Expected exception not thrown"); + } catch (IllegalArgumentException e) { + // pass + } catch (InvalidKeyException e) { + fail("Unexpected exception"); + } catch (URISyntaxException e) { + fail("Unexpected exception"); + } + + try { + CloudStorageAccount.parse(accountStringKeyNoDefaultProtocolPrimarySecondary); // no exception expected + } catch (Exception e) { + fail("Unexpected exception"); + } + + // SAS + + String accountStringSasPrimary = + String.format( + "DefaultEndpointsProtocol=https;AccountName=%1$s;SharedAccessSignature=%2$s;EndpointSuffix=%3$s;" + endpointCombination[0], + accountSasParams + ); + + String accountStringSasSecondary = + String.format( + "DefaultEndpointsProtocol=https;AccountName=%1$s;SharedAccessSignature=%2$s;EndpointSuffix=%3$s;" + endpointCombination[1], + accountSasParams + ); + + String accountStringSasPrimarySecondary = + String.format( + "DefaultEndpointsProtocol=https;AccountName=%1$s;SharedAccessSignature=%2$s;EndpointSuffix=%3$s;" + endpointCombination[2], + accountSasParams + ); + + try { + CloudStorageAccount.parse(accountStringSasPrimary); // no exception expected + } catch (Exception e) { + fail("Unexpected exception"); + } + + try { + CloudStorageAccount.parse(accountStringSasSecondary); + fail("Expected exception not thrown"); + } catch (IllegalArgumentException e) { + // pass + } catch (InvalidKeyException e) { + fail("Unexpected exception"); + } catch (URISyntaxException e) { + fail("Unexpected exception"); + } + + try { + CloudStorageAccount.parse(accountStringSasPrimarySecondary); // no exception expected + } catch (Exception e) { + fail("Unexpected exception"); + } + + // SAS, no default protocol + + String accountStringSasNoDefaultProtocolPrimary = + String.format( + "AccountName=%1$s;SharedAccessSignature=%2$s;EndpointSuffix=%3$s;" + endpointCombination[0], + accountSasParams + ); + + String accountStringSasNoDefaultProtocolSecondary = + String.format( + "AccountName=%1$s;SharedAccessSignature=%2$s;EndpointSuffix=%3$s;" + endpointCombination[1], + accountSasParams + ); + + String accountStringSasNoDefaultProtocolPrimarySecondary = + String.format( + "AccountName=%1$s;SharedAccessSignature=%2$s;EndpointSuffix=%3$s;" + endpointCombination[2], + accountSasParams + ); + + try { + CloudStorageAccount.parse(accountStringSasNoDefaultProtocolPrimary); // no exception expected + } catch (Exception e) { + fail("Unexpected exception"); + } + + try { + CloudStorageAccount.parse(accountStringSasNoDefaultProtocolSecondary); + fail("Expected exception not thrown"); + } catch (IllegalArgumentException e) { + // pass + } catch (InvalidKeyException e) { + fail("Unexpected exception"); + } catch (URISyntaxException e) { + fail("Unexpected exception"); + } + + try { + CloudStorageAccount.parse(accountStringSasNoDefaultProtocolPrimarySecondary); // no exception expected + } catch (Exception e) { + fail("Unexpected exception"); + } + + // SAS without AccountName + + String accountStringSasNoNameNoEndpoint = + String.format( + "SharedAccessSignature=%2$s", + accountSasParams + ); + + String accountStringSasNoNamePrimary = + String.format( + "SharedAccessSignature=%2$s;" + endpointCombination[0], + accountSasParams + ); + + String accountStringSasNoNameSecondary = + String.format( + "SharedAccessSignature=%2$s;" + endpointCombination[1], + accountSasParams + ); + + String accountStringSasNoNamePrimarySecondary = + String.format( + "SharedAccessSignature=%2$s;" + endpointCombination[2], + accountSasParams + ); + + try { + CloudStorageAccount.parse(accountStringSasNoNameNoEndpoint); + fail("Expected exception not thrown"); + } catch (IllegalArgumentException e) { + // pass + } catch (InvalidKeyException e) { + fail("Unexpected exception"); + } catch (URISyntaxException e) { + fail("Unexpected exception"); + } + + try { + CloudStorageAccount.parse(accountStringSasNoNamePrimary); // no exception expected + } catch (Exception e) { + fail("Unexpected exception"); + } + + try { + CloudStorageAccount.parse(accountStringSasNoNameSecondary); + fail("Expected exception not thrown"); + } catch (IllegalArgumentException e) { + // pass + } catch (InvalidKeyException e) { + fail("Unexpected exception"); + } catch (URISyntaxException e) { + fail("Unexpected exception"); + } + + try { + CloudStorageAccount.parse(accountStringSasNoNamePrimarySecondary); // no exception expected + } catch (Exception e) { + fail("Unexpected exception"); + } + } } - + private void connectionStringRoundtripHelper(String accountString) throws InvalidKeyException, URISyntaxException { CloudStorageAccount originalAccount = CloudStorageAccount.parse(accountString); String copiedAccountString = originalAccount.toString(true); - // assertEquals(accountString, copiedAccountString); CloudStorageAccount copiedAccount = CloudStorageAccount.parse(copiedAccountString); // make sure it round trips @@ -394,7 +779,7 @@ public void testCloudStorageAccountDevStoreFalsePlusAccountFails() fail(); } catch (IllegalArgumentException ex) { - assertEquals(SR.INVALID_CONNECTION_STRING_DEV_STORE_NOT_TRUE, ex.getMessage()); + assertEquals(SR.INVALID_CONNECTION_STRING, ex.getMessage()); } } @@ -407,7 +792,7 @@ public void testCloudStorageAccountDevStoreFalsePlusEndpointFails() fail(); } catch (IllegalArgumentException ex) { - assertEquals(SR.INVALID_CONNECTION_STRING_DEV_STORE_NOT_TRUE, ex.getMessage()); + assertEquals(SR.INVALID_CONNECTION_STRING, ex.getMessage()); } } @@ -420,7 +805,7 @@ public void testCloudStorageAccountDevStoreFalsePlusEndpointSuffixFails() fail(); } catch (IllegalArgumentException ex) { - assertEquals(SR.INVALID_CONNECTION_STRING_DEV_STORE_NOT_TRUE, ex.getMessage()); + assertEquals(SR.INVALID_CONNECTION_STRING, ex.getMessage()); } } @@ -504,8 +889,8 @@ public void testCloudStorageAccountDefaultCloudRoundtrip() @Test public void testCloudStorageAccountExplicitCloudRoundtrip() throws InvalidKeyException, URISyntaxException { - String accountString = "EndpointSuffix=a.b.c;BlobEndpoint=https://blobs/;AccountName=test;" - + "AccountKey=abc="; + String accountString = "EndpointSuffix=a.b.c;BlobEndpoint=https://blobs/;DefaultEndpointsProtocol=https;" + + "AccountName=test;AccountKey=abc="; assertEquals(accountString, CloudStorageAccount.parse(accountString).toString(true)); } @@ -541,7 +926,7 @@ public void testCloudStorageAccountEmptyValues() throws InvalidKeyException, URI String accountString = ";EndpointSuffix=a.b.c;;BlobEndpoint=http://blobs/;;" + "AccountName=test;;AccountKey=abc=;;;"; String validAccountString = "EndpointSuffix=a.b.c;BlobEndpoint=http://blobs/;" - + "AccountName=test;AccountKey=abc="; + + "DefaultEndpointsProtocol=http;AccountName=test;AccountKey=abc="; assertEquals(validAccountString, CloudStorageAccount.parse(accountString).toString(true)); } @@ -585,28 +970,28 @@ public void testCloudStorageAccountEndpointSuffix() @Test public void testCloudStorageAccountJustBlobToString() throws InvalidKeyException, URISyntaxException { - String accountString = "BlobEndpoint=http://blobs/;AccountName=test;AccountKey=abc="; + String accountString = "BlobEndpoint=http://blobs/;DefaultEndpointsProtocol=http;AccountName=test;AccountKey=abc="; assertEquals(accountString, CloudStorageAccount.parse(accountString).toString(true)); } @Test public void testCloudStorageAccountJustQueueToString() throws InvalidKeyException, URISyntaxException { - String accountString = "QueueEndpoint=http://queue/;AccountName=test;AccountKey=abc="; + String accountString = "QueueEndpoint=http://queue/;DefaultEndpointsProtocol=https;AccountName=test;AccountKey=abc="; assertEquals(accountString, CloudStorageAccount.parse(accountString).toString(true)); } @Test public void testCloudStorageAccountJustTableToString() throws InvalidKeyException, URISyntaxException { - String accountString = "TableEndpoint=http://table/;AccountName=test;AccountKey=abc="; + String accountString = "TableEndpoint=http://table/;DefaultEndpointsProtocol=https;AccountName=test;AccountKey=abc="; assertEquals(accountString, CloudStorageAccount.parse(accountString).toString(true)); } @Test public void testCloudStorageAccountJustFileToString() throws InvalidKeyException, URISyntaxException { - String accountString = "FileEndpoint=http://file/;AccountName=test;AccountKey=abc="; + String accountString = "FileEndpoint=http://file/;DefaultEndpointsProtocol=https;AccountName=test;AccountKey=abc="; assertEquals(accountString, CloudStorageAccount.parse(accountString).toString(true)); } diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/CloudStorageAccount.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/CloudStorageAccount.java index 1d62a34ab6f60..a03fd55f10e6e 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/CloudStorageAccount.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/CloudStorageAccount.java @@ -18,6 +18,7 @@ import java.net.URISyntaxException; import java.security.InvalidKeyException; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -75,6 +76,11 @@ public final class CloudStorageAccount { */ protected static final String BLOB_ENDPOINT_NAME = "BlobEndpoint"; + /** + * Represents the setting name for a custom blob storage secondary endpoint. + */ + protected static final String BLOB_SECONDARY_ENDPOINT_NAME = "BlobSecondaryEndpoint"; + /** * The setting name for using the default storage endpoints with the specified protocol. */ @@ -111,6 +117,11 @@ public final class CloudStorageAccount { */ private static final String FILE_ENDPOINT_NAME = "FileEndpoint"; + /** + * Represents the setting name for a custom file secondary endpoint. + */ + private static final String FILE_SECONDARY_ENDPOINT_NAME = "FileSecondaryEndpoint"; + /** * The format string for the primary endpoint. */ @@ -126,6 +137,11 @@ public final class CloudStorageAccount { */ protected static final String QUEUE_ENDPOINT_NAME = "QueueEndpoint"; + /** + * Represents the setting name for a custom queue secondary endpoint. + */ + protected static final String QUEUE_SECONDARY_ENDPOINT_NAME = "QueueSecondaryEndpoint"; + /** * Represents the setting name for a shared access key. */ @@ -136,6 +152,11 @@ public final class CloudStorageAccount { */ protected static final String TABLE_ENDPOINT_NAME = "TableEndpoint"; + /** + * Represents the setting name for a custom table storage secondary endpoint. + */ + protected static final String TABLE_SECONDARY_ENDPOINT_NAME = "TableSecondaryEndpoint"; + /** * The setting name for using the development storage. */ @@ -272,29 +293,31 @@ public static CloudStorageAccount parse(final String connectionString) throws UR * the connection settings. * @param service * A String that represents the service's base DNS name. - * @param serviceEndpoint + * @param serviceEndpointName * The service endpoint name to check in settings. + * @param serviceSecondaryEndpointName + * The service secondary endpoint name to check in settings. + * @param matchesAutomaticEndpointsSpec + * Whether the settings match the automatic endpoints specification. * @return The {@link StorageUri}. * @throws URISyntaxException */ private static StorageUri getStorageUri( - final Map settings, final String service, final String serviceEndpoint) + final Map settings, final String service, final String serviceEndpointName, final String serviceSecondaryEndpointName, final Boolean matchesAutomaticEndpointsSpec) throws URISyntaxException { - // Explicit Endpoint Case - if (settings.containsKey(serviceEndpoint)) { - return new StorageUri(new URI(settings.get(serviceEndpoint))); - } - // Automatic Endpoint Case - else if (settings.containsKey(DEFAULT_ENDPOINTS_PROTOCOL_NAME) && - settings.containsKey(CloudStorageAccount.ACCOUNT_NAME_NAME) && - settings.containsKey(CloudStorageAccount.ACCOUNT_KEY_NAME)) { + String serviceEndpoint = settingOrDefault(settings, serviceEndpointName); + String serviceSecondaryEndpoint = settingOrDefault(settings, serviceSecondaryEndpointName); + + if (serviceSecondaryEndpoint != null && serviceEndpoint != null) { + return new StorageUri(new URI(serviceEndpoint), new URI(serviceSecondaryEndpoint)); + } else if (serviceEndpoint != null) { + return new StorageUri(new URI(serviceEndpoint)); + } else if (matchesAutomaticEndpointsSpec) { final String scheme = settings.get(CloudStorageAccount.DEFAULT_ENDPOINTS_PROTOCOL_NAME); final String accountName = settings.get(CloudStorageAccount.ACCOUNT_NAME_NAME); final String endpointSuffix = settings.get(CloudStorageAccount.ENDPOINT_SUFFIX_NAME); return getDefaultStorageUri(scheme, accountName, getDNS(service, endpointSuffix)); - } - // Otherwise - else { + } else { return null; } } @@ -356,23 +379,199 @@ private static String getDNS(String service, String base) { */ private static CloudStorageAccount tryConfigureDevStore(final Map settings) throws URISyntaxException { - if (settings.containsKey(USE_DEVELOPMENT_STORAGE_NAME)) { + if (matchesSpecification( + settings, + allRequired(USE_DEVELOPMENT_STORAGE_NAME), + optional(DEVELOPMENT_STORAGE_PROXY_URI_NAME))) { if (!Boolean.parseBoolean(settings.get(USE_DEVELOPMENT_STORAGE_NAME))) { throw new IllegalArgumentException(SR.INVALID_CONNECTION_STRING_DEV_STORE_NOT_TRUE); } URI devStoreProxyUri = null; + if (settings.containsKey(DEVELOPMENT_STORAGE_PROXY_URI_NAME)) { devStoreProxyUri = new URI(settings.get(DEVELOPMENT_STORAGE_PROXY_URI_NAME)); } return getDevelopmentStorageAccount(devStoreProxyUri); - } - else { + } else { return null; } } + private interface ConnectionStringFilter { + Map apply(Map settings); + } + + private static ConnectionStringFilter allRequired(final String... settingNames) { + return new ConnectionStringFilter() { + @Override + public Map apply(Map settings) { + final Map result = new HashMap(settings); + + for (final String settingName : settingNames) { + if (result.containsKey(settingName)) { + result.remove(settingName); + } else { + return null; + } + } + + return result; + } + }; + } + + private static ConnectionStringFilter optional(final String... settingNames) { + return new ConnectionStringFilter() { + @Override + public Map apply(Map settings) { + final Map result = new HashMap(settings); + + for (final String settingName : settingNames) { + if (result.containsKey(settingName)) { + result.remove(settingName); + } + } + + return result; + } + }; + } + + private static ConnectionStringFilter atLeastOne(final String... settingNames) { + return new ConnectionStringFilter() { + @Override + public Map apply(Map settings) { + final Map result = new HashMap(settings); + Boolean foundOne = false; + + for (final String settingName : settingNames) { + if (result.containsKey(settingName)) { + result.remove(settingName); + foundOne = true; + } + } + + return foundOne ? result : null; + } + }; + } + + private static ConnectionStringFilter none(final String... settingNames) { + return new ConnectionStringFilter() { + @Override + public Map apply(Map settings) { + final Map result = new HashMap(settings); + Boolean foundOne = false; + + for (final String settingName : settingNames) { + if (result.containsKey(settingName)) { + result.remove(settingName); + foundOne = true; + } + } + + return foundOne ? null : result; + } + }; + } + + private static ConnectionStringFilter matchesAll(final ConnectionStringFilter... filters) { + return new ConnectionStringFilter() { + @Override + public Map apply(Map settings) { + Map result = new HashMap(settings); + + for (final ConnectionStringFilter filter : filters) { + if (result == null) { + break; + } + + result = filter.apply(result); + } + + return result; + } + }; + } + + private static ConnectionStringFilter matchesOne(final ConnectionStringFilter... filters) { + return new ConnectionStringFilter() { + @Override + public Map apply(Map settings) { + Map matchResult = null; + + for (final ConnectionStringFilter filter : filters) { + Map result = filter.apply(new HashMap(settings)); + + if (result != null) { + if (matchResult == null) { + matchResult = result; + } else { + return null; + } + } + } + + return matchResult; + } + }; + } + + private static ConnectionStringFilter matchesExactly(final ConnectionStringFilter filter) { + return new ConnectionStringFilter() { + @Override + public Map apply(Map settings) { + Map result = new HashMap(settings); + + result = filter.apply(result); + + if (result == null || !result.isEmpty()) { + return null; + } else { + return result; + } + } + }; + } + + private static ConnectionStringFilter validCredentials = + matchesOne( + matchesAll(allRequired(ACCOUNT_NAME_NAME, ACCOUNT_KEY_NAME), none(SHARED_ACCESS_SIGNATURE_NAME)), + matchesAll(allRequired(SHARED_ACCESS_SIGNATURE_NAME), optional(ACCOUNT_NAME_NAME), none(ACCOUNT_KEY_NAME)), + none(ACCOUNT_NAME_NAME, ACCOUNT_KEY_NAME, SHARED_ACCESS_SIGNATURE_NAME) + ); + + + private static Boolean matchesSpecification( + Map settings, + ConnectionStringFilter... constraints) { + for (ConnectionStringFilter constraint: constraints) { + Map remainingSettings = constraint.apply(settings); + + if (remainingSettings == null) { + return false; + } else { + settings = remainingSettings; + } + } + + if (settings.isEmpty()) { + return true; + } + + return false; + } + + private static Boolean isValidEndpointPair(String primary, String secondary) { + return (primary != null) || (/* primary is null, and... */ secondary == null); + } + + private static String settingOrDefault(Map settings, String key) { + return settings.containsKey(key) ? settings.get(key) : null; + } + /** * Evaluates connection settings and configures a CloudStorageAccount accordingly. * @@ -387,50 +586,97 @@ private static CloudStorageAccount tryConfigureDevStore(final Map settings) throws URISyntaxException, InvalidKeyException { - if (settings.containsKey(USE_DEVELOPMENT_STORAGE_NAME)) { - if (!Boolean.parseBoolean(settings.get(USE_DEVELOPMENT_STORAGE_NAME))) { - throw new IllegalArgumentException(SR.INVALID_CONNECTION_STRING_DEV_STORE_NOT_TRUE); - } - else { - return null; - } - } - String defaultEndpointSetting = settings.get(DEFAULT_ENDPOINTS_PROTOCOL_NAME); - if (defaultEndpointSetting != null) { - defaultEndpointSetting = defaultEndpointSetting.toLowerCase(); - if(!defaultEndpointSetting.equals(Constants.HTTP) - && !defaultEndpointSetting.equals(Constants.HTTPS)) { - return null; + ConnectionStringFilter endpointsOptional = + optional( + BLOB_ENDPOINT_NAME, BLOB_SECONDARY_ENDPOINT_NAME, + QUEUE_ENDPOINT_NAME, QUEUE_SECONDARY_ENDPOINT_NAME, + TABLE_ENDPOINT_NAME, TABLE_SECONDARY_ENDPOINT_NAME, + FILE_ENDPOINT_NAME, FILE_SECONDARY_ENDPOINT_NAME + ); + + ConnectionStringFilter primaryEndpointRequired = + atLeastOne( + BLOB_ENDPOINT_NAME, + QUEUE_ENDPOINT_NAME, + TABLE_ENDPOINT_NAME, + FILE_ENDPOINT_NAME + ); + + ConnectionStringFilter secondaryEndpointsOptional = + optional( + BLOB_SECONDARY_ENDPOINT_NAME, + QUEUE_SECONDARY_ENDPOINT_NAME, + TABLE_SECONDARY_ENDPOINT_NAME, + FILE_SECONDARY_ENDPOINT_NAME + ); + + ConnectionStringFilter automaticEndpointsMatchSpec = + matchesExactly(matchesAll( + matchesOne( + matchesAll(allRequired(ACCOUNT_KEY_NAME)), // Key + Name, Endpoints optional + allRequired(SHARED_ACCESS_SIGNATURE_NAME) // SAS + Name, Endpoints optional + ), + allRequired(ACCOUNT_NAME_NAME), // Name required to automatically create URIs + endpointsOptional, + optional(DEFAULT_ENDPOINTS_PROTOCOL_NAME, ENDPOINT_SUFFIX_NAME) + )); + + ConnectionStringFilter explicitEndpointsMatchSpec = + matchesExactly(matchesAll( // Any Credentials, Endpoints must be explicitly declared + validCredentials, + primaryEndpointRequired, + secondaryEndpointsOptional + )); + + Boolean matchesAutomaticEndpointsSpec = matchesSpecification(settings, automaticEndpointsMatchSpec); + Boolean matchesExplicitEndpointsSpec = matchesSpecification(settings, explicitEndpointsMatchSpec); + + if (matchesAutomaticEndpointsSpec || matchesExplicitEndpointsSpec) { + if (matchesAutomaticEndpointsSpec && !settings.containsKey(DEFAULT_ENDPOINTS_PROTOCOL_NAME)) { + settings.put(DEFAULT_ENDPOINTS_PROTOCOL_NAME, "https"); } - } - final StorageCredentials credentials = StorageCredentials.tryParseCredentials(settings); - final CloudStorageAccount account = new CloudStorageAccount(credentials, - getStorageUri(settings, SR.BLOB, BLOB_ENDPOINT_NAME), - getStorageUri(settings, SR.QUEUE, QUEUE_ENDPOINT_NAME), - getStorageUri(settings, SR.TABLE, TABLE_ENDPOINT_NAME), - getStorageUri(settings, SR.FILE, FILE_ENDPOINT_NAME)); - - // Invalid Account String - if ((account.getBlobEndpoint() == null) && (account.getFileEndpoint() == null) && - (account.getQueueEndpoint() == null) && (account.getTableEndpoint() == null)) { - return null; + String blobEndpoint = settingOrDefault(settings, BLOB_ENDPOINT_NAME); + String queueEndpoint = settingOrDefault(settings, QUEUE_ENDPOINT_NAME); + String tableEndpoint = settingOrDefault(settings, TABLE_ENDPOINT_NAME); + String fileEndpoint = settingOrDefault(settings, FILE_ENDPOINT_NAME); + String blobSecondaryEndpoint = settingOrDefault(settings, BLOB_SECONDARY_ENDPOINT_NAME); + String queueSecondaryEndpoint = settingOrDefault(settings, QUEUE_SECONDARY_ENDPOINT_NAME); + String tableSecondaryEndpoint = settingOrDefault(settings, TABLE_SECONDARY_ENDPOINT_NAME); + String fileSecondaryEndpoint = settingOrDefault(settings, FILE_SECONDARY_ENDPOINT_NAME); + + // if secondary is specified, primary must also be specified + + if ( + isValidEndpointPair(blobEndpoint, blobSecondaryEndpoint) + && isValidEndpointPair(queueEndpoint, queueSecondaryEndpoint) + && isValidEndpointPair(tableEndpoint, tableSecondaryEndpoint) + && isValidEndpointPair(fileEndpoint, fileSecondaryEndpoint) + ) { + CloudStorageAccount accountInformation = + new CloudStorageAccount( + StorageCredentials.tryParseCredentials(settings), + getStorageUri(settings, SR.BLOB, BLOB_ENDPOINT_NAME, BLOB_SECONDARY_ENDPOINT_NAME, matchesAutomaticEndpointsSpec), + getStorageUri(settings, SR.QUEUE, QUEUE_ENDPOINT_NAME, QUEUE_SECONDARY_ENDPOINT_NAME, matchesAutomaticEndpointsSpec), + getStorageUri(settings, SR.TABLE, TABLE_ENDPOINT_NAME, TABLE_SECONDARY_ENDPOINT_NAME, matchesAutomaticEndpointsSpec), + getStorageUri(settings, SR.FILE, FILE_ENDPOINT_NAME, FILE_SECONDARY_ENDPOINT_NAME, matchesAutomaticEndpointsSpec) + ); + + accountInformation.isBlobEndpointDefault = blobEndpoint == null; + accountInformation.isFileEndpointDefault = fileEndpoint == null; + accountInformation.isQueueEndpointDefault = queueEndpoint == null; + accountInformation.isTableEndpointDefault = tableEndpoint == null; + + accountInformation.endpointSuffix = settingOrDefault(settings, ENDPOINT_SUFFIX_NAME); + + accountInformation.accountName = settingOrDefault(settings, ACCOUNT_NAME_NAME); + + return accountInformation; + } } - // Endpoint is only default if it is neither null nor explicitly specified - account.isBlobEndpointDefault = !((account.getBlobEndpoint() == null) || - settings.containsKey(CloudStorageAccount.BLOB_ENDPOINT_NAME)); - account.isFileEndpointDefault = !((account.getFileEndpoint() == null) || - settings.containsKey(CloudStorageAccount.FILE_ENDPOINT_NAME)); - account.isQueueEndpointDefault = !((account.getQueueEndpoint() == null) || - settings.containsKey(CloudStorageAccount.QUEUE_ENDPOINT_NAME)); - account.isTableEndpointDefault = !((account.getTableEndpoint() == null) || - settings.containsKey(CloudStorageAccount.TABLE_ENDPOINT_NAME)); - - account.endpointSuffix = settings.get(CloudStorageAccount.ENDPOINT_SUFFIX_NAME); - - return account; + return null; } @@ -463,7 +709,12 @@ private static CloudStorageAccount tryConfigureServiceAccount(final Map values = new ArrayList(); + if (this.isDevStoreAccount) { values.add(String.format("%s=true", USE_DEVELOPMENT_STORAGE_NAME)); if (!this.getBlobEndpoint().toString().equals("http://127.0.0.1:10000/devstoreaccount1")) { @@ -1091,6 +1339,10 @@ public String toString(final boolean exportSecrets) { if (this.getCredentials() != null) { values.add(this.getCredentials().toString(exportSecrets)); } + + if (this.accountName != null && (this.getCredentials() != null ? this.getCredentials().getAccountName() == null : true)) { + values.add(String.format(attributeFormat, ACCOUNT_NAME_NAME, this.accountName)); + } } final StringBuilder returnString = new StringBuilder(); diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/StorageCredentials.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/StorageCredentials.java index 3b65558da5fa3..bb6b8d972f659 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/StorageCredentials.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/StorageCredentials.java @@ -68,7 +68,7 @@ protected static StorageCredentials tryParseCredentials(final Map Date: Thu, 7 Sep 2017 09:55:39 -0700 Subject: [PATCH 02/10] Revert "thread pool in BlobOutputStreamInternal should be daemon" --- .../blob/BlobOutputStreamInternal.java | 37 +++++-------------- 1 file changed, 9 insertions(+), 28 deletions(-) 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 0d3854373d764..44fde57d3f7a1 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,8 +27,13 @@ import java.util.HashSet; import java.util.Set; import java.util.UUID; -import java.util.concurrent.*; -import java.util.concurrent.atomic.AtomicInteger; +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 com.microsoft.azure.storage.AccessCondition; import com.microsoft.azure.storage.Constants; @@ -46,29 +51,6 @@ */ 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. */ @@ -189,10 +171,9 @@ private BlobOutputStreamInternal(final CloudBlob parentBlob, final AccessConditi this.threadExecutor = new ThreadPoolExecutor( this.options.getConcurrentRequestCount(), this.options.getConcurrentRequestCount(), - 10, + 10, TimeUnit.SECONDS, - new LinkedBlockingQueue(), - new BlobOutputStreamThreadFactory()); + new LinkedBlockingQueue()); this.completionService = new ExecutorCompletionService(this.threadExecutor); } From e3ec15cecd0615951bc1551c805ee342f578e42d Mon Sep 17 00:00:00 2001 From: Josh Friedman Date: Thu, 21 Sep 2017 13:13:07 -0700 Subject: [PATCH 03/10] Share snapshot support --- BreakingChanges.txt | 11 +- ChangeLog.txt | 3 + .../storage/file/CloudFileClientTests.java | 84 ++-- .../storage/file/CloudFileDirectoryTests.java | 130 +++--- .../storage/file/CloudFileShareTests.java | 366 ++++++++--------- .../azure/storage/file/CloudFileTests.java | 204 +++++----- .../azure/storage/file/CloudFile.java | 157 ++++--- .../azure/storage/file/CloudFileClient.java | 44 +- .../storage/file/CloudFileDirectory.java | 71 ++-- .../azure/storage/file/CloudFileShare.java | 382 +++++++++--------- .../azure/storage/file/FileRequest.java | 69 ++-- .../azure/storage/file/ShareListHandler.java | 2 +- 12 files changed, 801 insertions(+), 722 deletions(-) diff --git a/BreakingChanges.txt b/BreakingChanges.txt index d3cb92e4f0e6a..618c0ad387a82 100644 --- a/BreakingChanges.txt +++ b/BreakingChanges.txt @@ -1,4 +1,13 @@ -Changes in 5.1.1 +Changes in 6.0.0 + +FILE +* CloudFileShare constructor now takes a snapshotID String which can be null. + +* FileRequest get methods now takes a snapshotID String which can be null. + +* Changed listShares() ShareListingDetails parameter to be an enum set like what is done for listing blobs. + +Changes in 5.1.1 OTHER * Reverted the code from 5.1.0 because it contained a regression and an accidental change. diff --git a/ChangeLog.txt b/ChangeLog.txt index f0ce159c60467..5c229d60c1a64 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -1,3 +1,6 @@ +2017.XX.XX Version 6.0.0 + * Added support for taking a snapshot of a share. + 2017.08.28 Version 5.5.0 * Fixed a bug when using a SAS token where the token was being omitted from calls to delete a directory in the file service. * For Standard Storage Accounts only, added the ability to set the tier of individual block blobs. The tier can currently only be set through uploadTier() 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 10a0c5986e123..f83e4d588d3a5 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 @@ -115,46 +115,46 @@ public void testListSharesMaxResultsValidationTest() throws StorageException, UR assertNotNull(fileClient.listSharesSegmented("thereshouldntbeanyshareswiththisprefix")); } -// @Test -// public void testListSharesWithSnapshot() throws StorageException, URISyntaxException { -// CloudFileClient fileClient = FileTestHelper.createCloudFileClient(); -// CloudFileShare share = fileClient.getShareReference(UUID.randomUUID().toString()); -// share.create(); -// -// HashMap shareMeta = new HashMap(); -// shareMeta.put("key1", "value1"); -// share.setMetadata(shareMeta); -// share.uploadMetadata(); -// -// CloudFileShare snapshot = share.createSnapshot(); -// HashMap meta2 = new HashMap(); -// meta2.put("key2", "value2"); -// share.setMetadata(meta2); -// share.uploadMetadata(); -// -// CloudFileClient client = FileTestHelper.createCloudFileClient(); -// Iterable listResult = client.listShares(share.name, ShareListingDetails.ALL, null, null); -// -// int count = 0; -// boolean originalFound = false; -// boolean snapshotFound = false; -// for (CloudFileShare listShareItem : listResult) { -// if (listShareItem.getName().equals(share.getName()) && !listShareItem.isSnapshot() && !originalFound) -// { -// count++; -// originalFound = true; -// assertEquals(share.getMetadata(), listShareItem.getMetadata()); -// assertEquals(share.getStorageUri(), listShareItem.getStorageUri()); -// } -// else if (listShareItem.getName().equals(share.getName()) && -// listShareItem.isSnapshot() && !snapshotFound) { -// count++; -// snapshotFound = true; -// assertEquals(snapshot.getMetadata(), listShareItem.getMetadata()); -// assertEquals(snapshot.getStorageUri(), listShareItem.getStorageUri()); -// } -// } -// -// assertEquals(2, count); -// } + //@Test + public void testListSharesWithSnapshot() throws StorageException, URISyntaxException { + CloudFileClient fileClient = FileTestHelper.createCloudFileClient(); + CloudFileShare share = fileClient.getShareReference(UUID.randomUUID().toString()); + share.create(); + + HashMap shareMeta = new HashMap(); + shareMeta.put("key1", "value1"); + share.setMetadata(shareMeta); + share.uploadMetadata(); + + CloudFileShare snapshot = share.createSnapshot(); + HashMap meta2 = new HashMap(); + meta2.put("key2", "value2"); + share.setMetadata(meta2); + share.uploadMetadata(); + + CloudFileClient client = FileTestHelper.createCloudFileClient(); + Iterable listResult = client.listShares(share.name, ShareListingDetails.ALL, null, null); + + int count = 0; + boolean originalFound = false; + boolean snapshotFound = false; + for (CloudFileShare listShareItem : listResult) { + if (listShareItem.getName().equals(share.getName()) && !listShareItem.isSnapshot() && !originalFound) + { + count++; + originalFound = true; + assertEquals(share.getMetadata(), listShareItem.getMetadata()); + assertEquals(share.getStorageUri(), listShareItem.getStorageUri()); + } + else if (listShareItem.getName().equals(share.getName()) && + listShareItem.isSnapshot() && !snapshotFound) { + count++; + snapshotFound = true; + assertEquals(snapshot.getMetadata(), listShareItem.getMetadata()); + assertEquals(snapshot.getStorageUri(), listShareItem.getStorageUri()); + } + } + + assertEquals(2, count); + } } \ No newline at end of file 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 32f70df70d413..5da17d5f397d2 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 @@ -480,70 +480,70 @@ private static void testMetadataFailures(CloudFileDirectory directory, String ke directory.getMetadata().remove(key); } -// @Test -// public void testUnsupportedDirectoryApisWithinShareSnapshot() throws StorageException, URISyntaxException { -// CloudFileShare snapshot = this.share.createSnapshot(); -// CloudFileDirectory rootDir = snapshot.getRootDirectoryReference(); -// try { -// rootDir.create(); -// fail("Shouldn't get here"); -// } -// catch (IllegalArgumentException e) { -// assertEquals(SR.INVALID_OPERATION_FOR_A_SHARE_SNAPSHOT, e.getMessage()); -// } -// try { -// rootDir.delete(); -// fail("Shouldn't get here"); -// } -// catch (IllegalArgumentException e) { -// assertEquals(SR.INVALID_OPERATION_FOR_A_SHARE_SNAPSHOT, e.getMessage()); -// } -// try { -// rootDir.uploadMetadata(); -// fail("Shouldn't get here"); -// } -// catch (IllegalArgumentException e) { -// assertEquals(SR.INVALID_OPERATION_FOR_A_SHARE_SNAPSHOT, e.getMessage()); -// } -// -// snapshot.delete(); -// } - -// @Test -// public void testSupportedDirectoryApisInShareSnapshot() throws StorageException, URISyntaxException { -// CloudFileDirectory dir = this.share.getRootDirectoryReference().getDirectoryReference("dir1"); -// dir.deleteIfExists(); -// dir.create(); -// HashMap meta = new HashMap(); -// meta.put("key1", "value1"); -// dir.setMetadata(meta); -// dir.uploadMetadata(); -// CloudFileShare snapshot = this.share.createSnapshot(); -// CloudFileDirectory snapshotDir = snapshot.getRootDirectoryReference().getDirectoryReference("dir1"); -// -// HashMap meta2 = new HashMap(); -// meta2.put("key2", "value2"); -// dir.setMetadata(meta2); -// dir.uploadMetadata(); -// snapshotDir.downloadAttributes(); -// -// assertTrue(snapshotDir.getMetadata().size() == 1 && snapshotDir.getMetadata().get("key1").equals("value1")); -// assertNotNull(snapshotDir.getProperties().getEtag()); -// -// dir.downloadAttributes(); -// assertTrue(dir.getMetadata().size() == 1 && dir.getMetadata().get("key2").equals("value2")); -// assertNotNull(dir.getProperties().getEtag()); -// assertNotEquals(dir.getProperties().getEtag(), snapshotDir.getProperties().getEtag()); -// -// final UriQueryBuilder uriBuilder = new UriQueryBuilder(); -// uriBuilder.add("sharesnapshot", snapshot.snapshotID); -// uriBuilder.add("restype", "directory"); -// CloudFileDirectory snapshotDir2 = new CloudFileDirectory(uriBuilder.addToURI(dir.getUri()), this.share.getServiceClient().getCredentials()); -// assertEquals(snapshot.snapshotID, snapshotDir2.getShare().snapshotID); -// assertTrue(snapshotDir2.exists()); -// -// snapshot.delete(); -// } + //@Test + public void testUnsupportedDirectoryApisWithinShareSnapshot() throws StorageException, URISyntaxException { + CloudFileShare snapshot = this.share.createSnapshot(); + CloudFileDirectory rootDir = snapshot.getRootDirectoryReference(); + try { + rootDir.create(); + fail("Shouldn't get here"); + } + catch (IllegalArgumentException e) { + assertEquals(SR.INVALID_OPERATION_FOR_A_SHARE_SNAPSHOT, e.getMessage()); + } + try { + rootDir.delete(); + fail("Shouldn't get here"); + } + catch (IllegalArgumentException e) { + assertEquals(SR.INVALID_OPERATION_FOR_A_SHARE_SNAPSHOT, e.getMessage()); + } + try { + rootDir.uploadMetadata(); + fail("Shouldn't get here"); + } + catch (IllegalArgumentException e) { + assertEquals(SR.INVALID_OPERATION_FOR_A_SHARE_SNAPSHOT, e.getMessage()); + } + + snapshot.delete(); + } + + //@Test + public void testSupportedDirectoryApisInShareSnapshot() throws StorageException, URISyntaxException { + CloudFileDirectory dir = this.share.getRootDirectoryReference().getDirectoryReference("dir1"); + dir.deleteIfExists(); + dir.create(); + HashMap meta = new HashMap(); + meta.put("key1", "value1"); + dir.setMetadata(meta); + dir.uploadMetadata(); + CloudFileShare snapshot = this.share.createSnapshot(); + CloudFileDirectory snapshotDir = snapshot.getRootDirectoryReference().getDirectoryReference("dir1"); + + HashMap meta2 = new HashMap(); + meta2.put("key2", "value2"); + dir.setMetadata(meta2); + dir.uploadMetadata(); + snapshotDir.downloadAttributes(); + + assertTrue(snapshotDir.getMetadata().size() == 1 && snapshotDir.getMetadata().get("key1").equals("value1")); + assertNotNull(snapshotDir.getProperties().getEtag()); + + dir.downloadAttributes(); + assertTrue(dir.getMetadata().size() == 1 && dir.getMetadata().get("key2").equals("value2")); + assertNotNull(dir.getProperties().getEtag()); + assertNotEquals(dir.getProperties().getEtag(), snapshotDir.getProperties().getEtag()); + + final UriQueryBuilder uriBuilder = new UriQueryBuilder(); + uriBuilder.add("sharesnapshot", snapshot.snapshotID); + uriBuilder.add("restype", "directory"); + CloudFileDirectory snapshotDir2 = new CloudFileDirectory(uriBuilder.addToURI(dir.getUri()), this.share.getServiceClient().getCredentials()); + assertEquals(snapshot.snapshotID, snapshotDir2.getShare().snapshotID); + assertTrue(snapshotDir2.exists()); + + snapshot.delete(); + } /* [TestMethod] @@ -872,7 +872,7 @@ public void eventOccurred(SendingRequestEvent eventArg) { directory.delete(); assertFalse(directory.exists()); } - catch (StorageException e) { + catch (Exception e) { fail("Delete should succeed."); } } 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 544adeb110414..46a84aac4c581 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 @@ -494,195 +494,195 @@ public void eventOccurred(SendingRequestEvent eventArg) { assertTrue(this.share.deleteIfExists(null, null, ctx)); } -// @Test -// public void testCreateShareSnapshot() throws StorageException, URISyntaxException, IOException { -// // create share with metadata -// this.share.create(); -// assertTrue(this.share.exists()); -// HashMap shareMeta = new HashMap(); -// shareMeta.put("key1", "value1"); -// this.share.setMetadata(shareMeta); -// this.share.uploadMetadata(); -// -// CloudFileDirectory dir1 = this.share.getRootDirectoryReference().getDirectoryReference("dir1"); -// dir1.create(); -// CloudFile file1 = dir1.getFileReference("file1"); -// file1.create(1024); -// ByteArrayInputStream srcStream = FileTestHelper.getRandomDataStream(1024); -// file1.upload(srcStream, 1024); -// -// // create directory with metadata -// HashMap dirMeta = new HashMap(); -// dirMeta.put("key2", "value2"); -// dir1.setMetadata(dirMeta); -// dir1.uploadMetadata(); -// -// // verify that exists() call on snapshot populates metadata -// CloudFileShare snapshot = this.share.createSnapshot(); -// CloudFileClient client = FileTestHelper.createCloudFileClient(); -// CloudFileShare snapshotRef = client.getShareReference(snapshot.name, snapshot.snapshotID); -// assertTrue(snapshotRef.exists()); -// assertTrue(snapshotRef.getMetadata().size() == 1 && snapshotRef.getMetadata().get("key1").equals("value1")); -// -// // verify that downloadAttributes() populates metadata -// CloudFileShare snapshotRef2 = client.getShareReference(snapshot.name, snapshot.snapshotID); -// snapshotRef2.downloadAttributes(); -// snapshot.downloadAttributes(); -// assertTrue(snapshotRef2.getMetadata().size() == 1 && snapshotRef2.getMetadata().get("key1").equals("value1")); -// assertTrue(snapshot.getMetadata().size() == 1 && snapshot.getMetadata().get("key1").equals("value1")); -// -// // verify that exists() populates the metadata -// CloudFileDirectory snapshotDir1 = snapshot.getRootDirectoryReference().getDirectoryReference("dir1"); -// snapshotDir1.exists(); -// assertTrue(snapshotDir1.getMetadata().size() == 1 && snapshotDir1.getMetadata().get("key2").equals("value2")); -// -// // verify that downloadAttributes() populates the metadata -// CloudFileDirectory snapshotDir2 = snapshot.getRootDirectoryReference().getDirectoryReference("dir1"); -// snapshotDir2.downloadAttributes(); -// assertTrue(snapshotDir2.getMetadata().size() == 1 && snapshotDir2.getMetadata().get("key2").equals("value2")); -// -// // create snapshot with metadata -// HashMap shareMeta2 = new HashMap(); -// shareMeta2.put("abc", "def"); -// CloudFileShare snapshotRef3 = this.share.createSnapshot(shareMeta2, null, null, null); -// CloudFileShare snapshotRef4 = client.getShareReference(snapshotRef3.name, snapshotRef3.snapshotID); -// assertTrue(snapshotRef4.exists()); -// assertTrue(snapshotRef4.getMetadata().size() == 1 && snapshotRef4.getMetadata().get("abc").equals("def")); -// -// final UriQueryBuilder uriBuilder = new UriQueryBuilder(); -// uriBuilder.add("sharesnapshot", snapshot.snapshotID); -//// CloudFileShare snapshotRef5 = new CloudFileShare(uriBuilder.addToURI(this.share.getUri()), -//// this.share.getServiceClient().getCredentials(), null); -//// assertEquals(snapshot.snapshotID, snapshotRef5.snapshotID); -//// assertTrue(snapshotRef5.exists()); -// -// snapshot.delete(); -// } + //@Test + public void testCreateShareSnapshot() throws StorageException, URISyntaxException, IOException { + // create share with metadata + this.share.create(); + assertTrue(this.share.exists()); + HashMap shareMeta = new HashMap(); + shareMeta.put("key1", "value1"); + this.share.setMetadata(shareMeta); + this.share.uploadMetadata(); + + CloudFileDirectory dir1 = this.share.getRootDirectoryReference().getDirectoryReference("dir1"); + dir1.create(); + CloudFile file1 = dir1.getFileReference("file1"); + file1.create(1024); + ByteArrayInputStream srcStream = FileTestHelper.getRandomDataStream(1024); + file1.upload(srcStream, 1024); + + // create directory with metadata + HashMap dirMeta = new HashMap(); + dirMeta.put("key2", "value2"); + dir1.setMetadata(dirMeta); + dir1.uploadMetadata(); + + // verify that exists() call on snapshot populates metadata + CloudFileShare snapshot = this.share.createSnapshot(); + CloudFileClient client = FileTestHelper.createCloudFileClient(); + CloudFileShare snapshotRef = client.getShareReference(snapshot.name, snapshot.snapshotID); + assertTrue(snapshotRef.exists()); + assertTrue(snapshotRef.getMetadata().size() == 1 && snapshotRef.getMetadata().get("key1").equals("value1")); + + // verify that downloadAttributes() populates metadata + CloudFileShare snapshotRef2 = client.getShareReference(snapshot.name, snapshot.snapshotID); + snapshotRef2.downloadAttributes(); + snapshot.downloadAttributes(); + assertTrue(snapshotRef2.getMetadata().size() == 1 && snapshotRef2.getMetadata().get("key1").equals("value1")); + assertTrue(snapshot.getMetadata().size() == 1 && snapshot.getMetadata().get("key1").equals("value1")); + + // verify that exists() populates the metadata + CloudFileDirectory snapshotDir1 = snapshot.getRootDirectoryReference().getDirectoryReference("dir1"); + snapshotDir1.exists(); + assertTrue(snapshotDir1.getMetadata().size() == 1 && snapshotDir1.getMetadata().get("key2").equals("value2")); + + // verify that downloadAttributes() populates the metadata + CloudFileDirectory snapshotDir2 = snapshot.getRootDirectoryReference().getDirectoryReference("dir1"); + snapshotDir2.downloadAttributes(); + assertTrue(snapshotDir2.getMetadata().size() == 1 && snapshotDir2.getMetadata().get("key2").equals("value2")); + + // create snapshot with metadata + HashMap shareMeta2 = new HashMap(); + shareMeta2.put("abc", "def"); + CloudFileShare snapshotRef3 = this.share.createSnapshot(shareMeta2, null, null, null); + CloudFileShare snapshotRef4 = client.getShareReference(snapshotRef3.name, snapshotRef3.snapshotID); + assertTrue(snapshotRef4.exists()); + assertTrue(snapshotRef4.getMetadata().size() == 1 && snapshotRef4.getMetadata().get("abc").equals("def")); + + final UriQueryBuilder uriBuilder = new UriQueryBuilder(); + uriBuilder.add("sharesnapshot", snapshot.snapshotID); +// CloudFileShare snapshotRef5 = new CloudFileShare(uriBuilder.addToURI(this.share.getUri()), +// this.share.getServiceClient().getCredentials(), null); +// assertEquals(snapshot.snapshotID, snapshotRef5.snapshotID); +// assertTrue(snapshotRef5.exists()); + + snapshot.delete(); + } -// @Test -// public void testDeleteShareSnapshotOptions() throws StorageException, URISyntaxException, IOException { -// // create share with metadata -// this.share.create(); -// assertTrue(this.share.exists()); -// -// // verify that exists() call on snapshot populates metadata -// CloudFileShare snapshot = this.share.createSnapshot(); -// CloudFileClient client = FileTestHelper.createCloudFileClient(); -// CloudFileShare snapshotRef = client.getShareReference(snapshot.name, snapshot.snapshotID); -// assertTrue(snapshotRef.exists()); -// -// try { -// share.delete(); -// } -// catch (final StorageException e) { -// assertEquals(StorageErrorCodeStrings.SHARE_HAS_SNAPSHOTS, e.getErrorCode()); -// } -// -// share.delete(DeleteShareSnapshotsOption.INCLUDE_SNAPSHOTS, null, null, null); -// assertFalse(share.exists()); -// assertFalse(snapshot.exists()); -// } - -// @Test -// public void testListFilesAndDirectoriesWithinShareSnapshot() throws StorageException, URISyntaxException { -// this.share.create(); -// -// CloudFileDirectory myDir = this.share.getRootDirectoryReference().getDirectoryReference("mydir"); -// myDir.create(); -// myDir.getFileReference("myfile").create(1024); -// myDir.getDirectoryReference("yourDir").create(); -// assertTrue(this.share.exists()); -// -// CloudFileShare snapshot = this.share.createSnapshot(); -// CloudFileClient client = FileTestHelper.createCloudFileClient(); -// CloudFileShare snapshotRef = client.getShareReference(snapshot.name, snapshot.snapshotID); -// -// Iterable listResult = snapshotRef.getRootDirectoryReference().listFilesAndDirectories(); -// int count = 0; -// for (ListFileItem listFileItem : listResult) { -// count++; -// assertEquals("mydir", ((CloudFileDirectory) listFileItem).getName()); -// } -// -// assertEquals(1, count); -// -// count = 0; -// listResult = snapshotRef.getRootDirectoryReference().getDirectoryReference("mydir").listFilesAndDirectories(); -// for (ListFileItem listFileItem : listResult) { -// if (listFileItem instanceof CloudFileDirectory) { -// count++; -// assertEquals("yourDir", ((CloudFileDirectory) listFileItem).getName()); -// } -// else { -// count++; -// assertEquals("myfile", ((CloudFile) listFileItem).getName()); -// } -// } -// -// assertEquals(2, count); -// -// snapshot.delete(); -// } - -// @Test -// public void testUnsupportedApisShareSnapshot() throws StorageException, URISyntaxException { -// CloudFileClient client = FileTestHelper.createCloudFileClient(); -// this.share.create(); -// this.share.downloadPermissions(); -// CloudFileShare snapshot = this.share.createSnapshot(); -// try { -// snapshot.createSnapshot(); -// fail("Shouldn't get here"); -// } -// catch (IllegalArgumentException e) { -// assertEquals(SR.INVALID_OPERATION_FOR_A_SHARE_SNAPSHOT, e.getMessage()); -// } -//// try { -//// new CloudFileShare(snapshot.getQualifiedUri(), client.getCredentials(), "2016-10-24T16:37:17.0000000Z"); -//// fail("Shouldn't get here"); -//// } -//// catch (IllegalArgumentException e) { -//// assertEquals(SR.SNAPSHOT_QUERY_OPTION_ALREADY_DEFINED, e.getMessage()); -//// } -// try { -// snapshot.downloadPermissions(); -// fail("Shouldn't get here"); -// } -// catch (IllegalArgumentException e) { -// assertEquals(SR.INVALID_OPERATION_FOR_A_SHARE_SNAPSHOT, e.getMessage()); -// } -// try { -// snapshot.getStats(); -// fail("Shouldn't get here"); -// } -// catch (IllegalArgumentException e) { -// assertEquals(SR.INVALID_OPERATION_FOR_A_SHARE_SNAPSHOT, e.getMessage()); -// } -// try { -// snapshot.uploadMetadata(); -// fail("Shouldn't get here"); -// } -// catch (IllegalArgumentException e) { -// assertEquals(SR.INVALID_OPERATION_FOR_A_SHARE_SNAPSHOT, e.getMessage()); -// } -// try { -// FileSharePermissions permissions = new FileSharePermissions(); -// snapshot.uploadPermissions(permissions); -// fail("Shouldn't get here"); -// } -// catch (IllegalArgumentException e) { -// assertEquals(SR.INVALID_OPERATION_FOR_A_SHARE_SNAPSHOT, e.getMessage()); -// } + //@Test + public void testDeleteShareSnapshotOptions() throws StorageException, URISyntaxException, IOException { + // create share with metadata + this.share.create(); + assertTrue(this.share.exists()); + + // verify that exists() call on snapshot populates metadata + CloudFileShare snapshot = this.share.createSnapshot(); + CloudFileClient client = FileTestHelper.createCloudFileClient(); + CloudFileShare snapshotRef = client.getShareReference(snapshot.name, snapshot.snapshotID); + assertTrue(snapshotRef.exists()); + + try { + share.delete(); + } + catch (final StorageException e) { + assertEquals(StorageErrorCodeStrings.SHARE_HAS_SNAPSHOTS, e.getErrorCode()); + } + + share.delete(DeleteShareSnapshotsOption.INCLUDE_SNAPSHOTS, null, null, null); + assertFalse(share.exists()); + assertFalse(snapshot.exists()); + } + + //@Test + public void testListFilesAndDirectoriesWithinShareSnapshot() throws StorageException, URISyntaxException { + this.share.create(); + + CloudFileDirectory myDir = this.share.getRootDirectoryReference().getDirectoryReference("mydir"); + myDir.create(); + myDir.getFileReference("myfile").create(1024); + myDir.getDirectoryReference("yourDir").create(); + assertTrue(this.share.exists()); + + CloudFileShare snapshot = this.share.createSnapshot(); + CloudFileClient client = FileTestHelper.createCloudFileClient(); + CloudFileShare snapshotRef = client.getShareReference(snapshot.name, snapshot.snapshotID); + + Iterable listResult = snapshotRef.getRootDirectoryReference().listFilesAndDirectories(); + int count = 0; + for (ListFileItem listFileItem : listResult) { + count++; + assertEquals("mydir", ((CloudFileDirectory) listFileItem).getName()); + } + + assertEquals(1, count); + + count = 0; + listResult = snapshotRef.getRootDirectoryReference().getDirectoryReference("mydir").listFilesAndDirectories(); + for (ListFileItem listFileItem : listResult) { + if (listFileItem instanceof CloudFileDirectory) { + count++; + assertEquals("yourDir", ((CloudFileDirectory) listFileItem).getName()); + } + else { + count++; + assertEquals("myfile", ((CloudFile) listFileItem).getName()); + } + } + + assertEquals(2, count); + + snapshot.delete(); + } + + //@Test + public void testUnsupportedApisShareSnapshot() throws StorageException, URISyntaxException { + CloudFileClient client = FileTestHelper.createCloudFileClient(); + this.share.create(); + this.share.downloadPermissions(); + CloudFileShare snapshot = this.share.createSnapshot(); + try { + snapshot.createSnapshot(); + fail("Shouldn't get here"); + } + catch (IllegalArgumentException e) { + assertEquals(SR.INVALID_OPERATION_FOR_A_SHARE_SNAPSHOT, e.getMessage()); + } // try { -// snapshot.uploadProperties(); +// new CloudFileShare(snapshot.getQualifiedUri(), client.getCredentials(), "2016-10-24T16:37:17.0000000Z"); // fail("Shouldn't get here"); // } // catch (IllegalArgumentException e) { -// assertEquals(SR.INVALID_OPERATION_FOR_A_SHARE_SNAPSHOT, e.getMessage()); +// assertEquals(SR.SNAPSHOT_QUERY_OPTION_ALREADY_DEFINED, e.getMessage()); // } -// -// snapshot.delete(); -// } + try { + snapshot.downloadPermissions(); + fail("Shouldn't get here"); + } + catch (IllegalArgumentException e) { + assertEquals(SR.INVALID_OPERATION_FOR_A_SHARE_SNAPSHOT, e.getMessage()); + } + try { + snapshot.getStats(); + fail("Shouldn't get here"); + } + catch (IllegalArgumentException e) { + assertEquals(SR.INVALID_OPERATION_FOR_A_SHARE_SNAPSHOT, e.getMessage()); + } + try { + snapshot.uploadMetadata(); + fail("Shouldn't get here"); + } + catch (IllegalArgumentException e) { + assertEquals(SR.INVALID_OPERATION_FOR_A_SHARE_SNAPSHOT, e.getMessage()); + } + try { + FileSharePermissions permissions = new FileSharePermissions(); + snapshot.uploadPermissions(permissions); + fail("Shouldn't get here"); + } + catch (IllegalArgumentException e) { + assertEquals(SR.INVALID_OPERATION_FOR_A_SHARE_SNAPSHOT, e.getMessage()); + } + try { + snapshot.uploadProperties(); + fail("Shouldn't get here"); + } + catch (IllegalArgumentException e) { + assertEquals(SR.INVALID_OPERATION_FOR_A_SHARE_SNAPSHOT, e.getMessage()); + } + + snapshot.delete(); + } private static void assertPermissionsEqual(FileSharePermissions expected, FileSharePermissions actual) { HashMap expectedPolicies = expected.getSharedAccessPolicies(); 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 f4737435992bc..c2e1d77b787cb 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 @@ -1412,7 +1412,7 @@ public void eventOccurred(SendingRequestEvent eventArg) { file.delete(); assertFalse(file.exists()); } - catch (StorageException e) { + catch (Exception e) { fail("Delete should succeed."); } } @@ -1462,8 +1462,8 @@ public void testFileGetRangeContentMD5Bounds() throws StorageException, IOExcept FileRequestOptions options = new FileRequestOptions(); OperationContext opContext = new OperationContext(); try { - FileRequest.getFile(file.getUri(), options, opContext, null, 0L, 4L * Constants.MB, true); - FileRequest.getFile(file.getUri(), options, opContext, null, 0L, 4L * Constants.MB + 1, true); + FileRequest.getFile(file.getUri(), options, opContext, null, null, 0L, 4L * Constants.MB, true); + FileRequest.getFile(file.getUri(), options, opContext, null, null, 0L, 4L * Constants.MB + 1, true); fail("The request for range ContentMD5 should have thrown an Exception for exceeding the limit."); } catch (IllegalArgumentException e) @@ -1617,103 +1617,103 @@ private void doCloudFileCopy(boolean sourceIsSas, boolean destinationIsSas) source.delete(); } -// @Test -// public void testUnsupportedFileApisWithinShareSnapshot() throws StorageException, URISyntaxException { -// CloudFileShare snapshot = this.share.createSnapshot(); -// CloudFile file = snapshot.getRootDirectoryReference().getFileReference("file"); -// -// try { -// file.create(1024); -// fail("Shouldn't get here"); -// } -// catch (IllegalArgumentException e) { -// assertEquals(SR.INVALID_OPERATION_FOR_A_SHARE_SNAPSHOT, e.getMessage()); -// } -// try { -// file.delete(); -// fail("Shouldn't get here"); -// } -// catch (IllegalArgumentException e) { -// assertEquals(SR.INVALID_OPERATION_FOR_A_SHARE_SNAPSHOT, e.getMessage()); -// } -// try { -// file.uploadMetadata(); -// fail("Shouldn't get here"); -// } -// catch (IllegalArgumentException e) { -// assertEquals(SR.INVALID_OPERATION_FOR_A_SHARE_SNAPSHOT, e.getMessage()); -// } -// try { -// file.abortCopy(null); -// fail("Shouldn't get here"); -// } -// catch (IllegalArgumentException e) { -// assertEquals(SR.INVALID_OPERATION_FOR_A_SHARE_SNAPSHOT, e.getMessage()); -// } -// try { -// file.clearRange(0, 512); -// fail("Shouldn't get here"); -// } -// catch (IllegalArgumentException e) { -// assertEquals(SR.INVALID_OPERATION_FOR_A_SHARE_SNAPSHOT, e.getMessage()); -// } -// try { -// file.startCopy(file); -// fail("Shouldn't get here"); -// } -// catch (IllegalArgumentException e) { -// assertEquals(SR.INVALID_OPERATION_FOR_A_SHARE_SNAPSHOT, e.getMessage()); -// } -// try { -// file.upload(null, 512); -// fail("Shouldn't get here"); -// } -// catch (IllegalArgumentException e) { -// assertEquals(SR.INVALID_OPERATION_FOR_A_SHARE_SNAPSHOT, e.getMessage()); -// } catch (IOException e) { -// fail("Shouldn't get here"); -// } -// -// snapshot.delete(); -// } - -// @Test -// public void testSupportedFileApisInShareSnapshot() throws StorageException, URISyntaxException, UnsupportedEncodingException { -// CloudFileDirectory dir = this.share.getRootDirectoryReference().getDirectoryReference("dir1"); -// dir.deleteIfExists(); -// dir.create(); -// CloudFile file = dir.getFileReference("file"); -// file.create(1024); -// -// HashMap meta = new HashMap(); -// meta.put("key1", "value1"); -// file.setMetadata(meta); -// file.uploadMetadata(); -// -// CloudFileShare snapshot = this.share.createSnapshot(); -// CloudFile snapshotFile = snapshot.getRootDirectoryReference() -// .getDirectoryReference("dir1").getFileReference("file"); -// -// HashMap meta2 = new HashMap(); -// meta2.put("key2", "value2"); -// file.setMetadata(meta2); -// file.uploadMetadata(); -// snapshotFile.downloadAttributes(); -// -// assertTrue(snapshotFile.getMetadata().size() == 1 && snapshotFile.getMetadata().get("key1").equals("value1")); -// assertNotNull(snapshotFile.getProperties().getEtag()); -// -// file.downloadAttributes(); -// assertTrue(file.getMetadata().size() == 1 && file.getMetadata().get("key2").equals("value2")); -// assertNotNull(file.getProperties().getEtag()); -// assertNotEquals(file.getProperties().getEtag(), snapshotFile.getProperties().getEtag()); -// -// final UriQueryBuilder uriBuilder = new UriQueryBuilder(); -// uriBuilder.add("sharesnapshot", snapshot.snapshotID); -// CloudFile snapshotFile2 = new CloudFile(uriBuilder.addToURI(file.getUri()), this.share.getServiceClient().getCredentials()); -// assertEquals(snapshot.snapshotID, snapshotFile2.getShare().snapshotID); -// assertTrue(snapshotFile2.exists()); -// -// snapshot.delete(); -// } + //@Test + public void testUnsupportedFileApisWithinShareSnapshot() throws StorageException, URISyntaxException { + CloudFileShare snapshot = this.share.createSnapshot(); + CloudFile file = snapshot.getRootDirectoryReference().getFileReference("file"); + + try { + file.create(1024); + fail("Shouldn't get here"); + } + catch (IllegalArgumentException e) { + assertEquals(SR.INVALID_OPERATION_FOR_A_SHARE_SNAPSHOT, e.getMessage()); + } + try { + file.delete(); + fail("Shouldn't get here"); + } + catch (IllegalArgumentException e) { + assertEquals(SR.INVALID_OPERATION_FOR_A_SHARE_SNAPSHOT, e.getMessage()); + } + try { + file.uploadMetadata(); + fail("Shouldn't get here"); + } + catch (IllegalArgumentException e) { + assertEquals(SR.INVALID_OPERATION_FOR_A_SHARE_SNAPSHOT, e.getMessage()); + } + try { + file.abortCopy(null); + fail("Shouldn't get here"); + } + catch (IllegalArgumentException e) { + assertEquals(SR.INVALID_OPERATION_FOR_A_SHARE_SNAPSHOT, e.getMessage()); + } + try { + file.clearRange(0, 512); + fail("Shouldn't get here"); + } + catch (IllegalArgumentException e) { + assertEquals(SR.INVALID_OPERATION_FOR_A_SHARE_SNAPSHOT, e.getMessage()); + } + try { + file.startCopy(file); + fail("Shouldn't get here"); + } + catch (IllegalArgumentException e) { + assertEquals(SR.INVALID_OPERATION_FOR_A_SHARE_SNAPSHOT, e.getMessage()); + } + try { + file.upload(null, 512); + fail("Shouldn't get here"); + } + catch (IllegalArgumentException e) { + assertEquals(SR.INVALID_OPERATION_FOR_A_SHARE_SNAPSHOT, e.getMessage()); + } catch (IOException e) { + fail("Shouldn't get here"); + } + + snapshot.delete(); + } + + //@Test + public void testSupportedFileApisInShareSnapshot() throws StorageException, URISyntaxException, UnsupportedEncodingException { + CloudFileDirectory dir = this.share.getRootDirectoryReference().getDirectoryReference("dir1"); + dir.deleteIfExists(); + dir.create(); + CloudFile file = dir.getFileReference("file"); + file.create(1024); + + HashMap meta = new HashMap(); + meta.put("key1", "value1"); + file.setMetadata(meta); + file.uploadMetadata(); + + CloudFileShare snapshot = this.share.createSnapshot(); + CloudFile snapshotFile = snapshot.getRootDirectoryReference() + .getDirectoryReference("dir1").getFileReference("file"); + + HashMap meta2 = new HashMap(); + meta2.put("key2", "value2"); + file.setMetadata(meta2); + file.uploadMetadata(); + snapshotFile.downloadAttributes(); + + assertTrue(snapshotFile.getMetadata().size() == 1 && snapshotFile.getMetadata().get("key1").equals("value1")); + assertNotNull(snapshotFile.getProperties().getEtag()); + + file.downloadAttributes(); + assertTrue(file.getMetadata().size() == 1 && file.getMetadata().get("key2").equals("value2")); + assertNotNull(file.getProperties().getEtag()); + assertNotEquals(file.getProperties().getEtag(), snapshotFile.getProperties().getEtag()); + + final UriQueryBuilder uriBuilder = new UriQueryBuilder(); + uriBuilder.add("sharesnapshot", snapshot.snapshotID); + CloudFile snapshotFile2 = new CloudFile(uriBuilder.addToURI(file.getUri()), this.share.getServiceClient().getCredentials()); + assertEquals(snapshot.snapshotID, snapshotFile2.getShare().snapshotID); + assertTrue(snapshotFile2.exists()); + + snapshot.delete(); + } } \ No newline at end of file diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/file/CloudFile.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/file/CloudFile.java index 12063f87b5cb4..b22ece568cbc3 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/file/CloudFile.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/file/CloudFile.java @@ -155,8 +155,9 @@ public CloudFile(final StorageUri fileAbsoluteUri) throws StorageException, URIS * * @throws StorageException * If a storage service error occurred. + * @throws URISyntaxException */ - public CloudFile(final URI fileAbsoluteUri, final StorageCredentials credentials) throws StorageException { + public CloudFile(final URI fileAbsoluteUri, final StorageCredentials credentials) throws StorageException, URISyntaxException { this(new StorageUri(fileAbsoluteUri), credentials); } @@ -171,8 +172,9 @@ public CloudFile(final URI fileAbsoluteUri, final StorageCredentials credentials * * @throws StorageException * If a storage service error occurred. + * @throws URISyntaxException */ - public CloudFile(final StorageUri fileAbsoluteUri, final StorageCredentials credentials) throws StorageException { + public CloudFile(final StorageUri fileAbsoluteUri, final StorageCredentials credentials) throws StorageException, URISyntaxException { this.parseQueryAndVerify(fileAbsoluteUri, credentials); } @@ -230,9 +232,10 @@ protected CloudFile(final StorageUri uri, final String fileName, final CloudFile * * @throws StorageException * If a storage service error occurred. + * @throws URISyntaxException */ @DoesServiceRequest - public final void abortCopy(final String copyId) throws StorageException { + public final void abortCopy(final String copyId) throws StorageException, URISyntaxException { this.abortCopy(copyId, null /* accessCondition */, null /* options */, null /* opContext */); } @@ -253,16 +256,17 @@ public final void abortCopy(final String copyId) throws StorageException { * the operation. * * @throws StorageException - * If a storage service error occurred. + * If a storage service error occurred. + * @throws URISyntaxException */ @DoesServiceRequest public final void abortCopy(final String copyId, final AccessCondition accessCondition, FileRequestOptions options, - OperationContext opContext) throws StorageException { + OperationContext opContext) throws StorageException, URISyntaxException { if (opContext == null) { opContext = new OperationContext(); } - //this.getShare().assertNoSnapshot(); + this.getShare().assertNoSnapshot(); opContext.initialize(); options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient); @@ -429,9 +433,10 @@ public final String startCopy(final CloudFile sourceFile, final AccessCondition * * @throws StorageException * If a storage service error occurred. + * @throws URISyntaxException */ @DoesServiceRequest - public final String startCopy(final URI source) throws StorageException { + public final String startCopy(final URI source) throws StorageException, URISyntaxException { return this.startCopy(source, null /* sourceAccessCondition */, null /* destinationAccessCondition */, null /* options */, null /* opContext */); } @@ -459,17 +464,18 @@ public final String startCopy(final URI source) throws StorageException { * * @throws StorageException * If a storage service error occurred. + * @throws URISyntaxException * */ @DoesServiceRequest public final String startCopy(final URI source, final AccessCondition sourceAccessCondition, final AccessCondition destinationAccessCondition, FileRequestOptions options, OperationContext opContext) - throws StorageException { + throws StorageException, URISyntaxException { if (opContext == null) { opContext = new OperationContext(); } - //this.getShare().assertNoSnapshot(); + this.getShare().assertNoSnapshot(); opContext.initialize(); options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient); @@ -539,9 +545,10 @@ public String preProcessResponse(CloudFile file, CloudFileClient client, Operati * * @throws StorageException * If a storage service error occurred. + * @throws URISyntaxException */ @DoesServiceRequest - public void clearRange(final long offset, final long length) throws StorageException { + public void clearRange(final long offset, final long length) throws StorageException, URISyntaxException { this.clearRange(offset, length, null /* accessCondition */, null /* options */, null /* opContext */); } @@ -568,15 +575,16 @@ public void clearRange(final long offset, final long length) throws StorageExcep * * @throws StorageException * If a storage service error occurred. + * @throws URISyntaxException */ @DoesServiceRequest public void clearRange(final long offset, final long length, final AccessCondition accessCondition, - FileRequestOptions options, OperationContext opContext) throws StorageException { + FileRequestOptions options, OperationContext opContext) throws StorageException, URISyntaxException { if (opContext == null) { opContext = new OperationContext(); } - //this.getShare().assertNoSnapshot(); + this.getShare().assertNoSnapshot(); options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient); @@ -593,10 +601,11 @@ public void clearRange(final long offset, final long length, final AccessConditi * A long which represents the size, in bytes, of the file. * * @throws StorageException - * If a storage service error occurred. + * If a storage service error occurred. + * @throws URISyntaxException */ @DoesServiceRequest - public void create(final long size) throws StorageException { + public void create(final long size) throws StorageException, URISyntaxException { this.create(size, null /* accessCondition */, null /* options */, null /* opContext */); } @@ -619,16 +628,17 @@ public void create(final long size) throws StorageException { * * @throws StorageException * If a storage service error occurred. + * @throws URISyntaxException */ @DoesServiceRequest public void create(final long size, final AccessCondition accessCondition, FileRequestOptions options, - OperationContext opContext) throws StorageException { + OperationContext opContext) throws StorageException, URISyntaxException { if (opContext == null) { opContext = new OperationContext(); } - //this.getShare().assertNoSnapshot(); + this.getShare().assertNoSnapshot(); options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient); @@ -682,9 +692,10 @@ public Void preProcessResponse(CloudFile file, CloudFileClient client, Operation * * @throws StorageException * If a storage service error occurred. + * @throws URISyntaxException */ @DoesServiceRequest - public final void delete() throws StorageException { + public final void delete() throws StorageException, URISyntaxException { this.delete(null /* accessCondition */, null /* options */, null /* opContext */); } @@ -704,15 +715,16 @@ public final void delete() throws StorageException { * * @throws StorageException * If a storage service error occurred. + * @throws URISyntaxException */ @DoesServiceRequest public final void delete(final AccessCondition accessCondition, FileRequestOptions options, - OperationContext opContext) throws StorageException { + OperationContext opContext) throws StorageException, URISyntaxException { if (opContext == null) { opContext = new OperationContext(); } - //this.getShare().assertNoSnapshot(); + this.getShare().assertNoSnapshot(); opContext.initialize(); options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient); @@ -728,9 +740,11 @@ public final void delete(final AccessCondition accessCondition, FileRequestOptio * * @throws StorageException * If a storage service error occurred. + * @throws URISyntaxException + * */ @DoesServiceRequest - public final boolean deleteIfExists() throws StorageException { + public final boolean deleteIfExists() throws StorageException, URISyntaxException { return this.deleteIfExists(null /* accessCondition */, null /* options */, null /* opContext */); } @@ -752,12 +766,13 @@ public final boolean deleteIfExists() throws StorageException { * * @throws StorageException * If a storage service error occurred. + * @throws URISyntaxException */ @DoesServiceRequest public final boolean deleteIfExists(final AccessCondition accessCondition, FileRequestOptions options, - OperationContext opContext) throws StorageException { + OperationContext opContext) throws StorageException, URISyntaxException { options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient); - //this.getShare().assertNoSnapshot(); + this.getShare().assertNoSnapshot(); boolean exists = this.exists(true, accessCondition, options, opContext); if (exists) { @@ -1276,7 +1291,7 @@ public void setRequestLocationMode() { public HttpURLConnection buildRequest(CloudFileClient client, CloudFile file, OperationContext context) throws Exception { return FileRequest.getFileRanges(file.getTransformedAddress(context).getUri(this.getCurrentLocation()), - options, context, accessCondition); + options, context, accessCondition, file.getShare().snapshotID); } @Override @@ -1343,7 +1358,7 @@ public HttpURLConnection buildRequest(CloudFileClient client, CloudFile file, Op // : accessCondition; return FileRequest.getFile(file.getTransformedAddress(context).getUri(this.getCurrentLocation()), - options, context, accessCondition, this.getOffset(), this.getLength(), + options, context, accessCondition, file.getShare().snapshotID, this.getOffset(), this.getLength(), (options.getUseTransactionalContentMD5() && !this.getArePropertiesPopulated())); } @@ -1518,7 +1533,7 @@ public HttpURLConnection buildRequest(CloudFileClient client, CloudFile file, Op throws Exception { return FileRequest.getFileProperties( file.getTransformedAddress(context).getUri(this.getCurrentLocation()), - options, context, accessCondition); + options, context, accessCondition, file.getShare().snapshotID); } @Override @@ -1615,7 +1630,7 @@ public HttpURLConnection buildRequest(CloudFileClient client, CloudFile file, Op throws Exception { return FileRequest.getFileProperties( file.getTransformedAddress(context).getUri(this.getCurrentLocation()), - options, context, accessCondition); + options, context, accessCondition, file.getShare().snapshotID); } @Override @@ -1837,9 +1852,10 @@ public final FileInputStream openRead(final AccessCondition accessCondition, Fil * * @throws StorageException * If a storage service error occurred. + * @throws URISyntaxException */ @DoesServiceRequest - public FileOutputStream openWriteExisting() throws StorageException { + public FileOutputStream openWriteExisting() throws StorageException, URISyntaxException { return this .openOutputStreamInternal(null /* length */, null /* accessCondition */, null /* options */, null /* opContext */); } @@ -1863,10 +1879,11 @@ public FileOutputStream openWriteExisting() throws StorageException { * * @throws StorageException * If a storage service error occurred. + * @throws URISyntaxException */ @DoesServiceRequest public FileOutputStream openWriteExisting(AccessCondition accessCondition, FileRequestOptions options, - OperationContext opContext) throws StorageException { + OperationContext opContext) throws StorageException, URISyntaxException { return this.openOutputStreamInternal(null /* length */, accessCondition, options, opContext); } @@ -1885,9 +1902,10 @@ public FileOutputStream openWriteExisting(AccessCondition accessCondition, FileR * * @throws StorageException * If a storage service error occurred. + * @throws URISyntaxException */ @DoesServiceRequest - public FileOutputStream openWriteNew(final long length) throws StorageException { + public FileOutputStream openWriteNew(final long length) throws StorageException, URISyntaxException { return this .openOutputStreamInternal(length, null /* accessCondition */, null /* options */, null /* opContext */); } @@ -1917,10 +1935,11 @@ public FileOutputStream openWriteNew(final long length) throws StorageException * * @throws StorageException * If a storage service error occurred. + * @throws URISyntaxException */ @DoesServiceRequest public FileOutputStream openWriteNew(final long length, AccessCondition accessCondition, - FileRequestOptions options, OperationContext opContext) throws StorageException { + FileRequestOptions options, OperationContext opContext) throws StorageException, URISyntaxException { return openOutputStreamInternal(length, accessCondition, options, opContext); } @@ -1947,14 +1966,15 @@ public FileOutputStream openWriteNew(final long length, AccessCondition accessCo * * @throws StorageException * If a storage service error occurred. + * @throws URISyntaxException */ private FileOutputStream openOutputStreamInternal(Long length, AccessCondition accessCondition, - FileRequestOptions options, OperationContext opContext) throws StorageException { + FileRequestOptions options, OperationContext opContext) throws StorageException, URISyntaxException { if (opContext == null) { opContext = new OperationContext(); } - //this.getShare().assertNoSnapshot(); + this.getShare().assertNoSnapshot(); options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient, false /* setStartTime */); @@ -1990,9 +2010,10 @@ private FileOutputStream openOutputStreamInternal(Long length, AccessCondition a * @throws StorageException * If a storage service error occurred. * @throws IOException + * @throws URISyntaxException */ public void uploadFromByteArray(final byte[] buffer, final int offset, final int length) throws StorageException, - IOException { + IOException, URISyntaxException { uploadFromByteArray(buffer, offset, length, null /* accessCondition */, null /* options */, null /* opContext */); } @@ -2019,10 +2040,11 @@ public void uploadFromByteArray(final byte[] buffer, final int offset, final int * @throws StorageException * If a storage service error occurred. * @throws IOException + * @throws URISyntaxException */ public void uploadFromByteArray(final byte[] buffer, final int offset, final int length, final AccessCondition accessCondition, FileRequestOptions options, OperationContext opContext) - throws StorageException, IOException { + throws StorageException, IOException, URISyntaxException { ByteArrayInputStream inputStream = new ByteArrayInputStream(buffer, offset, length); this.upload(inputStream, length, accessCondition, options, opContext); inputStream.close(); @@ -2037,8 +2059,9 @@ public void uploadFromByteArray(final byte[] buffer, final int offset, final int * @throws StorageException * If a storage service error occurred. * @throws IOException + * @throws URISyntaxException */ - public void uploadFromFile(final String path) throws StorageException, IOException { + public void uploadFromFile(final String path) throws StorageException, IOException, URISyntaxException { uploadFromFile(path, null /* accessCondition */, null /* options */, null /* opContext */); } @@ -2061,9 +2084,10 @@ public void uploadFromFile(final String path) throws StorageException, IOExcepti * @throws StorageException * If a storage service error occurred. * @throws IOException + * @throws URISyntaxException */ public void uploadFromFile(final String path, final AccessCondition accessCondition, FileRequestOptions options, - OperationContext opContext) throws StorageException, IOException { + OperationContext opContext) throws StorageException, IOException, URISyntaxException { File file = new File(path); long fileLength = file.length(); InputStream inputStream = new BufferedInputStream(new java.io.FileInputStream(file)); @@ -2081,8 +2105,9 @@ public void uploadFromFile(final String path, final AccessCondition accessCondit * @throws StorageException * If a storage service error occurred. * @throws IOException + * @throws URISyntaxException */ - public void uploadText(final String content) throws StorageException, IOException { + public void uploadText(final String content) throws StorageException, IOException, URISyntaxException { this.uploadText(content, null /* charsetName */, null /* accessCondition */, null /* options */, null /* opContext */); } @@ -2109,9 +2134,10 @@ public void uploadText(final String content) throws StorageException, IOExceptio * @throws StorageException * If a storage service error occurred. * @throws IOException + * @throws URISyntaxException */ public void uploadText(final String content, final String charsetName, final AccessCondition accessCondition, - FileRequestOptions options, OperationContext opContext) throws StorageException, IOException { + FileRequestOptions options, OperationContext opContext) throws StorageException, IOException, URISyntaxException { byte[] bytes = (charsetName == null) ? content.getBytes() : content.getBytes(charsetName); this.uploadFromByteArray(bytes, 0, bytes.length, accessCondition, options, opContext); } @@ -2131,10 +2157,11 @@ public void uploadText(final String content, final String charsetName, final Acc * If an I/O exception occurred. * @throws StorageException * If a storage service error occurred. + * @throws URISyntaxException */ @DoesServiceRequest public void uploadRange(final InputStream sourceStream, final long offset, final long length) - throws StorageException, IOException { + throws StorageException, IOException, URISyntaxException { this.uploadRange(sourceStream, offset, length, null /* accessCondition */, null /* options */, null /* opContext */); } @@ -2163,16 +2190,17 @@ public void uploadRange(final InputStream sourceStream, final long offset, final * If an I/O exception occurred. * @throws StorageException * If a storage service error occurred. + * @throws URISyntaxException */ @DoesServiceRequest public void uploadRange(final InputStream sourceStream, final long offset, final long length, final AccessCondition accessCondition, FileRequestOptions options, OperationContext opContext) - throws StorageException, IOException { + throws StorageException, IOException, URISyntaxException { if (opContext == null) { opContext = new OperationContext(); } - //this.getShare().assertNoSnapshot(); + this.getShare().assertNoSnapshot(); options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient); @@ -2302,9 +2330,10 @@ public Void preProcessResponse(CloudFile file, CloudFileClient client, Operation * * @throws StorageException * If a storage service error occurred. + * @throws URISyntaxException */ @DoesServiceRequest - public final void uploadMetadata() throws StorageException { + public final void uploadMetadata() throws StorageException, URISyntaxException { this.uploadMetadata(null /* accessCondition */, null /* options */, null /* opContext */); } @@ -2328,16 +2357,17 @@ public final void uploadMetadata() throws StorageException { * * @throws StorageException * If a storage service error occurred. + * @throws URISyntaxException */ @DoesServiceRequest public final void uploadMetadata(final AccessCondition accessCondition, FileRequestOptions options, - OperationContext opContext) throws StorageException { + OperationContext opContext) throws StorageException, URISyntaxException { if (opContext == null) { opContext = new OperationContext(); } - //this.getShare().assertNoSnapshot(); + this.getShare().assertNoSnapshot(); opContext.initialize(); options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient); @@ -2395,9 +2425,10 @@ public Void preProcessResponse(CloudFile file, CloudFileClient client, Operation * * @throws StorageException * If a storage service error occurred. + * @throws URISyntaxException */ @DoesServiceRequest - public final void uploadProperties() throws StorageException { + public final void uploadProperties() throws StorageException, URISyntaxException { this.uploadProperties(null /* accessCondition */, null /* options */, null /*opContext */); } @@ -2420,15 +2451,16 @@ public final void uploadProperties() throws StorageException { * * @throws StorageException * If a storage service error occurred. + * @throws URISyntaxException */ @DoesServiceRequest public final void uploadProperties(final AccessCondition accessCondition, FileRequestOptions options, - OperationContext opContext) throws StorageException { + OperationContext opContext) throws StorageException, URISyntaxException { if (opContext == null) { opContext = new OperationContext(); } - //this.getShare().assertNoSnapshot(); + this.getShare().assertNoSnapshot(); opContext.initialize(); options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient); @@ -2482,8 +2514,9 @@ public Void preProcessResponse(CloudFile file, CloudFileClient client, Operation * * @throws StorageException * If a storage service error occurred. + * @throws URISyntaxException */ - public void resize(long size) throws StorageException { + public void resize(long size) throws StorageException, URISyntaxException { this.resize(size, null /* accessCondition */, null /* options */, null /* operationContext */); } @@ -2505,14 +2538,15 @@ public void resize(long size) throws StorageException { * * @throws StorageException * If a storage service error occurred. + * @throws URISyntaxException */ public void resize(long size, AccessCondition accessCondition, FileRequestOptions options, - OperationContext opContext) throws StorageException { + OperationContext opContext) throws StorageException, URISyntaxException { if (opContext == null) { opContext = new OperationContext(); } - //this.getShare().assertNoSnapshot(); + this.getShare().assertNoSnapshot(); opContext.initialize(); options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient); @@ -2569,9 +2603,10 @@ public Void preProcessResponse(CloudFile file, CloudFileClient client, Operation * If an I/O exception occurred. * @throws StorageException * If a storage service error occurred. + * @throws URISyntaxException */ @DoesServiceRequest - public void upload(final InputStream sourceStream, final long length) throws StorageException, IOException { + public void upload(final InputStream sourceStream, final long length) throws StorageException, IOException, URISyntaxException { this.upload(sourceStream, length, null /* accessCondition */, null /* options */, null /* opContext */); } @@ -2598,16 +2633,17 @@ public void upload(final InputStream sourceStream, final long length) throws Sto * @throws IOException * If an I/O exception occurred. * @throws StorageException - * If a storage service error occurred. + * If a storage service error occurred. + * @throws URISyntaxException */ @DoesServiceRequest public void upload(final InputStream sourceStream, final long length, final AccessCondition accessCondition, - FileRequestOptions options, OperationContext opContext) throws StorageException, IOException { + FileRequestOptions options, OperationContext opContext) throws StorageException, IOException, URISyntaxException { if (opContext == null) { opContext = new OperationContext(); } - //this.getShare().assertNoSnapshot(); + this.getShare().assertNoSnapshot(); options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient); @@ -2697,9 +2733,10 @@ protected static String getParentNameFromURI(final StorageUri resourceAddress, f * A {@link StorageCredentials} object used to authenticate access. * @throws StorageException * If a storage service error occurred. + * @throws URISyntaxException */ private void parseQueryAndVerify(final StorageUri completeUri, final StorageCredentials credentials) - throws StorageException { + throws StorageException, URISyntaxException { Utility.assertNotNull("completeUri", completeUri); if (!completeUri.isAbsolute()) { @@ -2725,12 +2762,12 @@ private void parseQueryAndVerify(final StorageUri completeUri, final StorageCred throw Utility.generateNewUnexpectedStorageException(e); } -// final HashMap queryParameters = PathUtility.parseQueryString(completeUri.getQuery()); + final HashMap queryParameters = PathUtility.parseQueryString(completeUri.getQuery()); -// final String[] snapshotIDs = queryParameters.get(Constants.QueryConstants.SHARE_SNAPSHOT); -// if (snapshotIDs != null && snapshotIDs.length > 0) { -// this.getShare().snapshotID = snapshotIDs[0]; -// } + final String[] snapshotIDs = queryParameters.get(Constants.QueryConstants.SHARE_SNAPSHOT); + if (snapshotIDs != null && snapshotIDs.length > 0) { + this.getShare().snapshotID = snapshotIDs[0]; + } } protected void updateEtagAndLastModifiedFromResponse(HttpURLConnection request) { diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/file/CloudFileClient.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/file/CloudFileClient.java index 4b3fb37aff472..409d4ff2730bf 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/file/CloudFileClient.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/file/CloudFileClient.java @@ -101,30 +101,30 @@ public CloudFileClient(StorageUri storageUri, StorageCredentials credentials) { */ public CloudFileShare getShareReference(final String shareName) throws URISyntaxException, StorageException { Utility.assertNotNullOrEmpty("shareName", shareName); - return new CloudFileShare(shareName, this); + return this.getShareReference(shareName, null); } -// /** -// * Gets a {@link CloudFileShare} object with the specified name. -// * -// * @param shareName -// * The name of the share, which must adhere to share naming rules. The share name should not -// * include any path separator characters (/). -// * Share names must be lowercase, between 3-63 characters long and must start with a letter or -// * number. Share names may contain only letters, numbers, and the dash (-) character. -// * @param snapshotID -// * A String that represents the snapshot ID of the share. -// * @return A reference to a {@link CloudFileShare} object. -// * @throws StorageException -// * @throws URISyntaxException -// * -// * @see Naming and Referencing Shares, -// * Directories, Files, and Metadata -// */ -// protected CloudFileShare getShareReference(final String shareName, String snapshotID) throws URISyntaxException, StorageException { -// Utility.assertNotNullOrEmpty("shareName", shareName); -// return new CloudFileShare(shareName, snapshotID, this); -// } + /** + * Gets a {@link CloudFileShare} object with the specified name. + * + * @param shareName + * The name of the share, which must adhere to share naming rules. The share name should not + * include any path separator characters (/). + * Share names must be lowercase, between 3-63 characters long and must start with a letter or + * number. Share names may contain only letters, numbers, and the dash (-) character. + * @param snapshotID + * A String that represents the snapshot ID of the share. + * @return A reference to a {@link CloudFileShare} object. + * @throws StorageException + * @throws URISyntaxException + * + * @see Naming and Referencing Shares, + * Directories, Files, and Metadata + */ + protected CloudFileShare getShareReference(final String shareName, String snapshotID) throws URISyntaxException, StorageException { + Utility.assertNotNullOrEmpty("shareName", shareName); + return new CloudFileShare(shareName, snapshotID, this); + } /** * Returns an enumerable collection of shares for this File service client. diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/file/CloudFileDirectory.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/file/CloudFileDirectory.java index 3507b014805e9..9eaae67ca2ba5 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/file/CloudFileDirectory.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/file/CloudFileDirectory.java @@ -95,8 +95,9 @@ public final class CloudFileDirectory implements ListFileItem { * @param directoryAbsoluteUri * A {@link URI} that represents the file directory's address. * @throws StorageException + * @throws URISyntaxException */ - public CloudFileDirectory(final URI directoryAbsoluteUri) throws StorageException { + public CloudFileDirectory(final URI directoryAbsoluteUri) throws StorageException, URISyntaxException { this(new StorageUri(directoryAbsoluteUri)); } @@ -106,8 +107,9 @@ public CloudFileDirectory(final URI directoryAbsoluteUri) throws StorageExceptio * @param directoryAbsoluteUri * A {@link StorageUri} that represents the file directory's address. * @throws StorageException + * @throws URISyntaxException */ - public CloudFileDirectory(final StorageUri directoryAbsoluteUri) throws StorageException { + public CloudFileDirectory(final StorageUri directoryAbsoluteUri) throws StorageException, URISyntaxException { this(directoryAbsoluteUri, (StorageCredentials) null); } @@ -120,9 +122,10 @@ public CloudFileDirectory(final StorageUri directoryAbsoluteUri) throws StorageE * @param credentials * A {@link StorageCredentials} object used to authenticate access. * @throws StorageException + * @throws URISyntaxException */ public CloudFileDirectory(final URI directoryAbsoluteUri, final StorageCredentials credentials) - throws StorageException { + throws StorageException, URISyntaxException { this(new StorageUri(directoryAbsoluteUri), credentials); } @@ -135,9 +138,10 @@ public CloudFileDirectory(final URI directoryAbsoluteUri, final StorageCredentia * @param credentials * A {@link StorageCredentials} object used to authenticate access. * @throws StorageException + * @throws URISyntaxException */ public CloudFileDirectory(final StorageUri directoryAbsoluteUri, final StorageCredentials credentials) - throws StorageException { + throws StorageException, URISyntaxException { this.parseQueryAndVerify(directoryAbsoluteUri, credentials); } @@ -168,9 +172,10 @@ protected CloudFileDirectory(final StorageUri uri, final String directoryName, f * * @throws StorageException * If a storage service error occurred. + * @throws URISyntaxException */ @DoesServiceRequest - public void create() throws StorageException { + public void create() throws StorageException, URISyntaxException { this.create(null /* options */, null /* opContext */); } @@ -188,14 +193,15 @@ public void create() throws StorageException { * * @throws StorageException * If a storage service error occurred. + * @throws URISyntaxException */ @DoesServiceRequest - public void create(FileRequestOptions options, OperationContext opContext) throws StorageException { + public void create(FileRequestOptions options, OperationContext opContext) throws StorageException, URISyntaxException { if (opContext == null) { opContext = new OperationContext(); } - //this.getShare().assertNoSnapshot(); + this.getShare().assertNoSnapshot(); opContext.initialize(); options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient); @@ -254,9 +260,10 @@ public Void preProcessResponse(CloudFileDirectory directory, CloudFileClient cli * * @throws StorageException * If a storage service error occurred. + * @throws URISyntaxException */ @DoesServiceRequest - public boolean createIfNotExists() throws StorageException { + public boolean createIfNotExists() throws StorageException, URISyntaxException { return this.createIfNotExists(null /* options */, null /* opContext */); } @@ -276,12 +283,13 @@ public boolean createIfNotExists() throws StorageException { * * @throws StorageException * If a storage service error occurred. + * @throws URISyntaxException */ @DoesServiceRequest - public boolean createIfNotExists(FileRequestOptions options, OperationContext opContext) throws StorageException { + public boolean createIfNotExists(FileRequestOptions options, OperationContext opContext) throws StorageException, URISyntaxException { options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient); - //this.getShare().assertNoSnapshot(); + this.getShare().assertNoSnapshot(); boolean exists = this.exists(true /* primaryOnly */, null /* accessCondition */, options, opContext); if (exists) { @@ -309,9 +317,10 @@ public boolean createIfNotExists(FileRequestOptions options, OperationContext op * * @throws StorageException * If a storage service error occurred. + * @throws URISyntaxException */ @DoesServiceRequest - public void delete() throws StorageException { + public void delete() throws StorageException, URISyntaxException { this.delete(null /* accessCondition */, null /* options */, null /* opContext */); } @@ -331,15 +340,16 @@ public void delete() throws StorageException { * * @throws StorageException * If a storage service error occurred. + * @throws URISyntaxException */ @DoesServiceRequest public void delete(AccessCondition accessCondition, FileRequestOptions options, OperationContext opContext) - throws StorageException { + throws StorageException, URISyntaxException { if (opContext == null) { opContext = new OperationContext(); } - //this.getShare().assertNoSnapshot(); + this.getShare().assertNoSnapshot(); opContext.initialize(); options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient); @@ -386,9 +396,10 @@ public Void preProcessResponse(CloudFileDirectory directory, CloudFileClient cli * * @throws StorageException * If a storage service error occurred. + * @throws URISyntaxException */ @DoesServiceRequest - public boolean deleteIfExists() throws StorageException { + public boolean deleteIfExists() throws StorageException, URISyntaxException { return this.deleteIfExists(null /* accessCondition */, null /* options */, null /* opContext */); } @@ -410,10 +421,11 @@ public boolean deleteIfExists() throws StorageException { * * @throws StorageException * If a storage service error occurred. + * @throws URISyntaxException */ @DoesServiceRequest public boolean deleteIfExists(AccessCondition accessCondition, FileRequestOptions options, - OperationContext opContext) throws StorageException { + OperationContext opContext) throws StorageException, URISyntaxException { options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient); boolean exists = this.exists(true /* primaryOnly */, accessCondition, options, opContext); @@ -506,7 +518,7 @@ public HttpURLConnection buildRequest(CloudFileClient client, CloudFileDirectory OperationContext context) throws Exception { return FileRequest.getDirectoryProperties( directory.getTransformedAddress().getUri(this.getCurrentLocation()), options, context, - accessCondition); + accessCondition, directory.getShare().snapshotID); } @Override @@ -560,9 +572,10 @@ private void updatePropertiesFromResponse(HttpURLConnection request) { * * @throws StorageException * If a storage service error occurred. + * @throws URISyntaxException */ @DoesServiceRequest - public void uploadMetadata() throws StorageException { + public void uploadMetadata() throws StorageException, URISyntaxException { this.uploadMetadata(null /* accessCondition */, null /* options */, null /* opContext */); } @@ -582,15 +595,16 @@ public void uploadMetadata() throws StorageException { * * @throws StorageException * If a storage service error occurred. + * @throws URISyntaxException */ @DoesServiceRequest public void uploadMetadata(AccessCondition accessCondition, FileRequestOptions options, OperationContext opContext) - throws StorageException { + throws StorageException, URISyntaxException { if (opContext == null) { opContext = new OperationContext(); } - //this.getShare().assertNoSnapshot(); + this.getShare().assertNoSnapshot(); opContext.initialize(); options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient); @@ -696,7 +710,7 @@ public HttpURLConnection buildRequest(CloudFileClient client, CloudFileDirectory OperationContext context) throws Exception { return FileRequest.getDirectoryProperties( directory.getTransformedAddress().getUri(this.getCurrentLocation()), options, context, - accessCondition); + accessCondition, directory.getShare().snapshotID); } @Override @@ -917,7 +931,7 @@ public HttpURLConnection buildRequest(CloudFileClient client, CloudFileDirectory .getNextMarker() : null); return FileRequest.listFilesAndDirectories( directory.getTransformedAddress().getUri(this.getCurrentLocation()), - options, context, listingContext); + options, context, listingContext, directory.getShare().snapshotID); } @Override @@ -1153,9 +1167,10 @@ protected void setStorageUri(final StorageUri storageUri) { * A {@link StorageCredentials} object used to authenticate access. * @throws StorageException * If a storage service error occurred. + * @throws URISyntaxException */ private void parseQueryAndVerify(final StorageUri completeUri, final StorageCredentials credentials) - throws StorageException { + throws StorageException, URISyntaxException { Utility.assertNotNull("completeUri", completeUri); if (!completeUri.isAbsolute()) { @@ -1181,12 +1196,12 @@ private void parseQueryAndVerify(final StorageUri completeUri, final StorageCred throw Utility.generateNewUnexpectedStorageException(e); } -// final HashMap queryParameters = PathUtility.parseQueryString(completeUri.getQuery()); -// -// final String[] snapshotIDs = queryParameters.get(Constants.QueryConstants.SHARE_SNAPSHOT); -// if (snapshotIDs != null && snapshotIDs.length > 0) { -// this.getShare().snapshotID = snapshotIDs[0]; -// } + final HashMap queryParameters = PathUtility.parseQueryString(completeUri.getQuery()); + + final String[] snapshotIDs = queryParameters.get(Constants.QueryConstants.SHARE_SNAPSHOT); + if (snapshotIDs != null && snapshotIDs.length > 0) { + this.getShare().snapshotID = snapshotIDs[0]; + } } /** diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/file/CloudFileShare.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/file/CloudFileShare.java index 20b618ccb568e..d3d180261010e 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/file/CloudFileShare.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/file/CloudFileShare.java @@ -81,7 +81,7 @@ public final class CloudFileShare { /** * Holds the snapshot ID. */ - //String snapshotID; + String snapshotID; /** * Holds a reference to the associated service client. @@ -111,14 +111,14 @@ public final class CloudFileShare { * @see Naming and Referencing Shares, * Directories, Files, and Metadata */ - protected CloudFileShare(final String shareName, /*String snapshotID,*/ final CloudFileClient client) throws URISyntaxException, + protected CloudFileShare(final String shareName, String snapshotID, final CloudFileClient client) throws URISyntaxException, StorageException { Utility.assertNotNull("client", client); Utility.assertNotNull("shareName", shareName); this.storageUri = PathUtility.appendPathToUri(client.getStorageUri(), shareName); this.name = shareName; - //this.snapshotID = snapshotID; + this.snapshotID = snapshotID; this.fileServiceClient = client; } @@ -155,6 +155,8 @@ public CloudFileShare(final StorageUri storageUri) throws StorageException { * A java.net.URI object that represents the absolute URI of the share. * @param credentials * A {@link StorageCredentials} object used to authenticate access. + * @param snapshotID + * A String that represents the snapshot version, if applicable. * * @throws StorageException * If a storage service error occurred. @@ -220,7 +222,7 @@ public void create(FileRequestOptions options, OperationContext opContext) throw opContext = new OperationContext(); } - //assertNoSnapshot(); + assertNoSnapshot(); if (this.properties != null && this.properties.getShareQuota() != null) { Utility.assertInBounds("Share Quota", this.properties.getShareQuota(), 1, FileConstants.MAX_SHARE_QUOTA); } @@ -416,7 +418,7 @@ private StorageRequest deleteImpl( public HttpURLConnection buildRequest( CloudFileClient client, CloudFileShare share, OperationContext context) throws Exception { return FileRequest.deleteShare( - share.getTransformedAddress().getPrimaryUri(), options, context, accessCondition); + share.getTransformedAddress().getPrimaryUri(), options, context, accessCondition, share.snapshotID, deleteSnapshotsOption); } @Override @@ -588,7 +590,7 @@ public void setRequestLocationMode() { public HttpURLConnection buildRequest(CloudFileClient client, CloudFileShare share, OperationContext context) throws Exception { return FileRequest.getShareProperties(share.getTransformedAddress().getUri(this.getCurrentLocation()), - options, context, accessCondition); + options, context, accessCondition, share.snapshotID); } @Override @@ -656,7 +658,7 @@ public FileSharePermissions downloadPermissions(AccessCondition accessCondition, opContext = new OperationContext(); } - //assertNoSnapshot(); + assertNoSnapshot(); opContext.initialize(); options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient); @@ -717,135 +719,135 @@ public FileSharePermissions postProcessResponse(HttpURLConnection connection, return getRequest; } -// /** -// * Creates a snapshot of the share. -// * -// * @return A CloudFileShare object that represents the snapshot of the share. -// * -// * @throws StorageException -// * If a storage service error occurred. -// */ -// @DoesServiceRequest -// protected final CloudFileShare createSnapshot() throws StorageException { -// return this -// .createSnapshot(null /* metadata */, null /* accessCondition */, null /* options */, null /* opContext */); -// } -// -// /** -// * Creates a snapshot of the file share using the specified request options and operation context. -// * -// * @param accessCondition -// * An {@link AccessCondition} object that represents the access conditions for the share. -// * @param options -// * A {@link FileRequestOptions} object that specifies any additional options for the request. Specifying -// * null will use the default request options from the associated service client ( -// * {@link CloudFileClient}). -// * @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 CloudFileShare object that represents the snapshot of the file share. -// * -// * @throws StorageException -// * If a storage service error occurred. -// */ -// @DoesServiceRequest -// protected final CloudFileShare createSnapshot(final AccessCondition accessCondition, FileRequestOptions options, -// OperationContext opContext) throws StorageException { -// return this.createSnapshot(null /* metadata */, accessCondition, options, opContext); -// } -// -// /** -// * Creates a snapshot of the file share using the specified request options and operation context. -// * -// * @param metadata -// * A collection of name-value pairs defining the metadata of the snapshot, or null. -// * @param accessCondition -// * An {@link AccessCondition} object that represents the access conditions for the file share. -// * @param options -// * A {@link FileRequestOptions} object that specifies any additional options for the request. Specifying -// * null will use the default request options from the associated service client ( -// * {@link CloudFileClient}). -// * @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 CloudFileShare object that represents the snapshot of the file share. -// * -// * @throws StorageException -// * If a storage service error occurred. -// */ -// @DoesServiceRequest -// protected final CloudFileShare createSnapshot(final HashMap metadata, -// final AccessCondition accessCondition, FileRequestOptions options, OperationContext opContext) -// throws StorageException { -// //assertNoSnapshot(); -// -// if (opContext == null) { -// opContext = new OperationContext(); -// } -// -// opContext.initialize(); -// options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient); -// -// return ExecutionEngine -// .executeWithRetry(this.fileServiceClient, this, -// this.createSnapshotImpl(metadata, accessCondition, options), options.getRetryPolicyFactory(), -// opContext); -// } - -// private StorageRequest createSnapshotImpl( -// final HashMap metadata, final AccessCondition accessCondition, -// final FileRequestOptions options) { -// final StorageRequest putRequest = -// new StorageRequest( -// options, this.getStorageUri()) { -// -// @Override -// public HttpURLConnection buildRequest(CloudFileClient client, CloudFileShare share, OperationContext context) -// throws Exception { -// return FileRequest.snapshotShare(share.getTransformedAddress().getUri(this.getCurrentLocation()), -// options, context, accessCondition); -// } -// -// @Override -// public void setHeaders(HttpURLConnection connection, CloudFileShare share, OperationContext context) { -// if (metadata != null) { -// FileRequest.addMetadata(connection, metadata, context); -// } -// } -// -// @Override -// public void signRequest(HttpURLConnection connection, CloudFileClient client, OperationContext context) -// throws Exception { -// StorageRequest.signBlobQueueAndFileRequest(connection, client, 0L, context); -// } -// -// @Override -// public CloudFileShare preProcessResponse(CloudFileShare share, CloudFileClient client, OperationContext context) -// throws Exception { -// if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_CREATED) { -// this.setNonExceptionedRetryableFailure(true); -// return null; -// } -// -// final String snapshotTime = FileResponse.getSnapshotTime(this.getConnection()); -// CloudFileShare snapshot = new CloudFileShare(share.getName(), snapshotTime, client); -// snapshot.setProperties(new FileShareProperties(share.properties)); -// -// // use the specified metadata if not null : otherwise share's metadata -// snapshot.setMetadata(metadata != null ? metadata : share.metadata); -// -// snapshot.updatePropertiesFromResponse(this.getConnection()); -// -// return snapshot; -// } -// }; -// -// return putRequest; -// } + /** + * Creates a snapshot of the share. + * + * @return A CloudFileShare object that represents the snapshot of the share. + * + * @throws StorageException + * If a storage service error occurred. + */ + @DoesServiceRequest + protected final CloudFileShare createSnapshot() throws StorageException { + return this + .createSnapshot(null /* metadata */, null /* accessCondition */, null /* options */, null /* opContext */); + } + + /** + * Creates a snapshot of the file share using the specified request options and operation context. + * + * @param accessCondition + * An {@link AccessCondition} object that represents the access conditions for the share. + * @param options + * A {@link FileRequestOptions} object that specifies any additional options for the request. Specifying + * null will use the default request options from the associated service client ( + * {@link CloudFileClient}). + * @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 CloudFileShare object that represents the snapshot of the file share. + * + * @throws StorageException + * If a storage service error occurred. + */ + @DoesServiceRequest + protected final CloudFileShare createSnapshot(final AccessCondition accessCondition, FileRequestOptions options, + OperationContext opContext) throws StorageException { + return this.createSnapshot(null /* metadata */, accessCondition, options, opContext); + } + + /** + * Creates a snapshot of the file share using the specified request options and operation context. + * + * @param metadata + * A collection of name-value pairs defining the metadata of the snapshot, or null. + * @param accessCondition + * An {@link AccessCondition} object that represents the access conditions for the file share. + * @param options + * A {@link FileRequestOptions} object that specifies any additional options for the request. Specifying + * null will use the default request options from the associated service client ( + * {@link CloudFileClient}). + * @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 CloudFileShare object that represents the snapshot of the file share. + * + * @throws StorageException + * If a storage service error occurred. + */ + @DoesServiceRequest + protected final CloudFileShare createSnapshot(final HashMap metadata, + final AccessCondition accessCondition, FileRequestOptions options, OperationContext opContext) + throws StorageException { + assertNoSnapshot(); + + if (opContext == null) { + opContext = new OperationContext(); + } + + opContext.initialize(); + options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient); + + return ExecutionEngine + .executeWithRetry(this.fileServiceClient, this, + this.createSnapshotImpl(metadata, accessCondition, options), options.getRetryPolicyFactory(), + opContext); + } + + private StorageRequest createSnapshotImpl( + final HashMap metadata, final AccessCondition accessCondition, + final FileRequestOptions options) { + final StorageRequest putRequest = + new StorageRequest( + options, this.getStorageUri()) { + + @Override + public HttpURLConnection buildRequest(CloudFileClient client, CloudFileShare share, OperationContext context) + throws Exception { + return FileRequest.snapshotShare(share.getTransformedAddress().getUri(this.getCurrentLocation()), + options, context, accessCondition); + } + + @Override + public void setHeaders(HttpURLConnection connection, CloudFileShare share, OperationContext context) { + if (metadata != null) { + FileRequest.addMetadata(connection, metadata, context); + } + } + + @Override + public void signRequest(HttpURLConnection connection, CloudFileClient client, OperationContext context) + throws Exception { + StorageRequest.signBlobQueueAndFileRequest(connection, client, 0L, context); + } + + @Override + public CloudFileShare preProcessResponse(CloudFileShare share, CloudFileClient client, OperationContext context) + throws Exception { + if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_CREATED) { + this.setNonExceptionedRetryableFailure(true); + return null; + } + + final String snapshotTime = FileResponse.getSnapshotTime(this.getConnection()); + CloudFileShare snapshot = new CloudFileShare(share.getName(), snapshotTime, client); + snapshot.setProperties(new FileShareProperties(share.properties)); + + // use the specified metadata if not null : otherwise share's metadata + snapshot.setMetadata(metadata != null ? metadata : share.metadata); + + snapshot.updatePropertiesFromResponse(this.getConnection()); + + return snapshot; + } + }; + + return putRequest; + } /** * Queries the service for this share's {@link ShareStats}. @@ -883,7 +885,7 @@ public ShareStats getStats(FileRequestOptions options, OperationContext opContex opContext = new OperationContext(); } - //assertNoSnapshot(); + assertNoSnapshot(); opContext.initialize(); options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient); @@ -1004,7 +1006,7 @@ public void setRequestLocationMode() { public HttpURLConnection buildRequest(CloudFileClient client, CloudFileShare share, OperationContext context) throws Exception { return FileRequest.getShareProperties(share.getTransformedAddress().getUri(this.getCurrentLocation()), - options, context, accessCondition); + options, context, accessCondition, share.snapshotID); } @Override @@ -1057,14 +1059,14 @@ private void updatePropertiesFromResponse(HttpURLConnection request) { } } -// /** -// * Asserts that the share is not a snapshot. -// */ -// protected void assertNoSnapshot() { -// if (isSnapshot()) { -// throw new IllegalArgumentException(SR.INVALID_OPERATION_FOR_A_SHARE_SNAPSHOT); -// } -// } + /** + * Asserts that the share is not a snapshot. + */ + protected void assertNoSnapshot() { + if (isSnapshot()) { + throw new IllegalArgumentException(SR.INVALID_OPERATION_FOR_A_SHARE_SNAPSHOT); + } + } /** * Returns a shared access signature for the share. Note this does not contain the leading "?". @@ -1173,7 +1175,7 @@ public void uploadMetadata() throws StorageException { @DoesServiceRequest public void uploadMetadata(AccessCondition accessCondition, FileRequestOptions options, OperationContext opContext) throws StorageException { - //assertNoSnapshot(); + assertNoSnapshot(); if (opContext == null) { opContext = new OperationContext(); @@ -1263,7 +1265,7 @@ public final void uploadProperties() throws StorageException { public final void uploadProperties( AccessCondition accessCondition, FileRequestOptions options, OperationContext opContext) throws StorageException { - //assertNoSnapshot(); + assertNoSnapshot(); if (this.properties != null && this.properties.getShareQuota() != null) { Utility.assertInBounds("Share Quota", this.properties.getShareQuota(), 1, FileConstants.MAX_SHARE_QUOTA); @@ -1350,7 +1352,7 @@ public void uploadPermissions(final FileSharePermissions permissions) throws Sto @DoesServiceRequest public void uploadPermissions(final FileSharePermissions permissions, final AccessCondition accessCondition, FileRequestOptions options, OperationContext opContext) throws StorageException { - //assertNoSnapshot(); + assertNoSnapshot(); if (opContext == null) { opContext = new OperationContext(); @@ -1452,12 +1454,12 @@ private void parseQueryAndVerify(final StorageUri completeUri, final StorageCred this.storageUri = PathUtility.stripURIQueryAndFragment(completeUri); -// final HashMap queryParameters = PathUtility.parseQueryString(completeUri.getQuery()); + final HashMap queryParameters = PathUtility.parseQueryString(completeUri.getQuery()); -// final String[] snapshotIDs = queryParameters.get(Constants.QueryConstants.SHARE_SNAPSHOT); -// if (snapshotIDs != null && snapshotIDs.length > 0) { -// this.snapshotID = snapshotIDs[0]; -// } + final String[] snapshotIDs = queryParameters.get(Constants.QueryConstants.SHARE_SNAPSHOT); + if (snapshotIDs != null && snapshotIDs.length > 0) { + this.snapshotID = snapshotIDs[0]; + } final StorageCredentialsSharedAccessSignature parsedCredentials = SharedAccessSignatureHelper.parseQuery(completeUri); @@ -1495,25 +1497,25 @@ public URI getUri() { return this.storageUri.getPrimaryUri(); } -// /** -// * Returns the snapshotID for this share. -// * -// * @return The snapshotID as a string for this share. -// */ -// protected final String getSnapshot() { -// return this.snapshotID; -// } -// -// /** -// * Indicates whether this share is a snapshot. -// * -// * @return true if the share is a snapshot, otherwise false. -// * -// * @see DeleteSnapshotsOption -// */ -// protected final boolean isSnapshot() { -// return this.snapshotID != null; -// } + /** + * Returns the snapshotID for this share. + * + * @return The snapshotID as a string for this share. + */ + protected final String getSnapshot() { + return this.snapshotID; + } + + /** + * Indicates whether this share is a snapshot. + * + * @return true if the share is a snapshot, otherwise false. + * + * @see DeleteSnapshotsOption + */ + protected final boolean isSnapshot() { + return this.snapshotID != null; + } /** * Returns the list of URIs for all locations. @@ -1524,23 +1526,23 @@ public StorageUri getStorageUri() { return this.storageUri; } -// /** -// * Returns the snapshot or shared access signature qualified URI for this share. -// * -// * @return A java.net.URI object that represents the snapshot or shared access signature. -// * -// * @throws StorageException -// * If a storage service error occurred. -// * @throws URISyntaxException -// * If the resource URI is invalid. -// */ -// public final URI getQualifiedUri() throws URISyntaxException, StorageException { -// if (this.isSnapshot()) { -// return PathUtility.addToQuery(this.getUri(), String.format("sharesnapshot=%s", this.snapshotID)); -// } -// -// return this.fileServiceClient.getCredentials().transformUri(this.getUri()); -// } + /** + * Returns the snapshot or shared access signature qualified URI for this share. + * + * @return A java.net.URI object that represents the snapshot or shared access signature. + * + * @throws StorageException + * If a storage service error occurred. + * @throws URISyntaxException + * If the resource URI is invalid. + */ + public final URI getQualifiedUri() throws URISyntaxException, StorageException { + if (this.isSnapshot()) { + return PathUtility.addToQuery(this.getUri(), String.format("sharesnapshot=%s", this.snapshotID)); + } + + return this.fileServiceClient.getCredentials().transformUri(this.getUri()); + } /** * Returns the name of the share. diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/file/FileRequest.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/file/FileRequest.java index fc29553cf84c7..092f4866a6d62 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/file/FileRequest.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/file/FileRequest.java @@ -131,7 +131,7 @@ private static void addProperties(final HttpURLConnection request, FilePropertie * the share snapshot version to the query builder. * @throws StorageException */ - protected static void addShareSnapshot(final UriQueryBuilder builder, final String snapshotVersion) + public static void addShareSnapshot(final UriQueryBuilder builder, final String snapshotVersion) throws StorageException { if (snapshotVersion != null) { builder.add(Constants.QueryConstants.SHARE_SNAPSHOT, snapshotVersion); @@ -288,26 +288,26 @@ public static HttpURLConnection deleteFile(final URI uri, final FileRequestOptio * @throws IllegalArgumentException */ public static HttpURLConnection deleteShare(final URI uri, final FileRequestOptions fileOptions, - final OperationContext opContext, final AccessCondition accessCondition) + final OperationContext opContext, final AccessCondition accessCondition, String snapshotVersion, DeleteShareSnapshotsOption deleteSnapshotsOption) throws IOException, URISyntaxException, StorageException { final UriQueryBuilder shareBuilder = getShareUriQueryBuilder(); - //FileRequest.addShareSnapshot(shareBuilder, snapshotVersion); + FileRequest.addShareSnapshot(shareBuilder, snapshotVersion); HttpURLConnection request = BaseRequest.delete(uri, fileOptions, shareBuilder, opContext); if (accessCondition != null) { accessCondition.applyConditionToRequest(request); } -// switch (deleteSnapshotsOption) { -// case NONE: -// // nop -// break; -// case INCLUDE_SNAPSHOTS: -// request.setRequestProperty(Constants.HeaderConstants.DELETE_SNAPSHOT_HEADER, -// Constants.HeaderConstants.INCLUDE_SNAPSHOTS_VALUE); -// break; -// default: -// break; -// } + switch (deleteSnapshotsOption) { + case NONE: + // nop + break; + case INCLUDE_SNAPSHOTS: + request.setRequestProperty(Constants.HeaderConstants.DELETE_SNAPSHOT_HEADER, + Constants.HeaderConstants.INCLUDE_SNAPSHOTS_VALUE); + break; + default: + break; + } return request; } @@ -362,6 +362,8 @@ public static HttpURLConnection getAcl(final URI uri, final FileRequestOptions f * the operation. * @param accessCondition * An {@link AccessCondition} object that represents the access conditions for the file. + * @param snapshotVersion + * The snapshot version, if the share is a snapshot. * @param offset * The offset at which to begin returning content. * @param count @@ -378,7 +380,7 @@ public static HttpURLConnection getAcl(final URI uri, final FileRequestOptions f * @throws IllegalArgumentException */ public static HttpURLConnection getFile(final URI uri, final FileRequestOptions fileOptions, - final OperationContext opContext, final AccessCondition accessCondition, final Long offset, + final OperationContext opContext, final AccessCondition accessCondition, final String snapshotVersion, final Long offset, final Long count, boolean requestRangeContentMD5) throws IOException, URISyntaxException, StorageException { if (offset != null && requestRangeContentMD5) { @@ -387,7 +389,7 @@ public static HttpURLConnection getFile(final URI uri, final FileRequestOptions } final UriQueryBuilder builder = new UriQueryBuilder(); - //FileRequest.addShareSnapshot(builder, snapshotVersion); + FileRequest.addShareSnapshot(builder, snapshotVersion); final HttpURLConnection request = BaseRequest.createURLConnection(uri, fileOptions, builder, opContext); request.setRequestMethod(Constants.HTTP_GET); @@ -432,6 +434,8 @@ public static HttpURLConnection getFile(final URI uri, final FileRequestOptions * @param accessCondition * An {@link AccessCondition} object that represents the access conditions for the file. * @return a HttpURLConnection to use to perform the operation. + * @param snapshotVersion + * the snapshot version to the query builder. * @throws IOException * if there is an error opening the connection * @throws URISyntaxException @@ -441,11 +445,11 @@ public static HttpURLConnection getFile(final URI uri, final FileRequestOptions * @throws IllegalArgumentException */ public static HttpURLConnection getFileProperties(final URI uri, final FileRequestOptions fileOptions, - final OperationContext opContext, final AccessCondition accessCondition) throws StorageException, + final OperationContext opContext, final AccessCondition accessCondition, final String snapshotVersion) throws StorageException, IOException, URISyntaxException { final UriQueryBuilder builder = new UriQueryBuilder(); - return getProperties(uri, fileOptions, opContext, accessCondition, builder); + return getProperties(uri, fileOptions, opContext, accessCondition, builder, snapshotVersion); } /** @@ -475,11 +479,11 @@ public static HttpURLConnection getFileProperties(final URI uri, final FileReque * @throws IllegalArgumentException */ public static HttpURLConnection getFileRanges(final URI uri, final FileRequestOptions fileOptions, - final OperationContext opContext, final AccessCondition accessCondition) throws StorageException, + final OperationContext opContext, final AccessCondition accessCondition, final String snapshotVersion) throws StorageException, IOException, URISyntaxException { final UriQueryBuilder builder = new UriQueryBuilder(); - //addShareSnapshot(builder, snapshotVersion); + addShareSnapshot(builder, snapshotVersion); builder.add(Constants.QueryConstants.COMPONENT, RANGE_LIST_QUERY_ELEMENT_NAME); final HttpURLConnection request = BaseRequest.createURLConnection(uri, fileOptions, builder, opContext); @@ -507,15 +511,17 @@ public static HttpURLConnection getFileRanges(final URI uri, final FileRequestOp * the operation. * @param accessCondition * An {@link AccessCondition} object that represents the access conditions for the share. + * @param snapshotVersion + * the snapshot version to the query builder. * @return a HttpURLConnection configured for the operation. * @throws StorageException * */ public static HttpURLConnection getShareProperties(final URI uri, final FileRequestOptions fileOptions, - final OperationContext opContext, AccessCondition accessCondition) throws IOException, URISyntaxException, + final OperationContext opContext, AccessCondition accessCondition, final String snapshotVersion) throws IOException, URISyntaxException, StorageException { final UriQueryBuilder shareBuilder = getShareUriQueryBuilder(); - return getProperties(uri, fileOptions, opContext, accessCondition, shareBuilder); + return getProperties(uri, fileOptions, opContext, accessCondition, shareBuilder, snapshotVersion); } /** @@ -598,13 +604,16 @@ private static UriQueryBuilder getDirectoryUriQueryBuilder() throws StorageExcep * the operation. * @param accessCondition * An {@link AccessCondition} object that represents the access conditions for the share. + * @param snapshotVersion + * the snapshot version to the query builder. * @return a HttpURLConnection configured for the operation. * @throws StorageException * */ private static HttpURLConnection getProperties(final URI uri, final FileRequestOptions fileOptions, - final OperationContext opContext, AccessCondition accessCondition, final UriQueryBuilder builder) + final OperationContext opContext, AccessCondition accessCondition, final UriQueryBuilder builder, + String snapshotVersion) throws IOException, URISyntaxException, StorageException { - //addShareSnapshot(builder, snapshotVersion); + addShareSnapshot(builder, snapshotVersion); HttpURLConnection request = BaseRequest.getProperties(uri, fileOptions, builder, opContext); if (accessCondition != null) { @@ -801,14 +810,16 @@ public static HttpURLConnection deleteDirectory(final URI uri, final FileRequest * the operation. * @param accessCondition * An {@link AccessCondition} object that represents the access conditions for the directory. + * @param snapshotVersion + * the snapshot version to the query builder. * @return a HttpURLConnection configured for the operation. * @throws StorageException * */ public static HttpURLConnection getDirectoryProperties(final URI uri, final FileRequestOptions fileOptions, - final OperationContext opContext, AccessCondition accessCondition) throws IOException, URISyntaxException, + final OperationContext opContext, AccessCondition accessCondition, String snapshotVersion) throws IOException, URISyntaxException, StorageException { final UriQueryBuilder directoryBuilder = getDirectoryUriQueryBuilder(); - return getProperties(uri, fileOptions, opContext, accessCondition, directoryBuilder); + return getProperties(uri, fileOptions, opContext, accessCondition, directoryBuilder, snapshotVersion); } /** @@ -827,6 +838,8 @@ public static HttpURLConnection getDirectoryProperties(final URI uri, final File * the operation. * @param listingContext * A set of parameters for the listing operation. + * @param snapshotVersion + * the snapshot version to the query builder. * @return a HttpURLConnection configured for the operation. * @throws IOException * @throws URISyntaxException @@ -834,11 +847,11 @@ public static HttpURLConnection getDirectoryProperties(final URI uri, final File * @throws IllegalArgumentException */ public static HttpURLConnection listFilesAndDirectories(final URI uri, final FileRequestOptions fileOptions, - final OperationContext opContext, final ListingContext listingContext) throws URISyntaxException, + final OperationContext opContext, final ListingContext listingContext, String snapshotVersion) throws URISyntaxException, IOException, StorageException { final UriQueryBuilder builder = getDirectoryUriQueryBuilder(); - //addShareSnapshot(builder, snapshotVersion); + addShareSnapshot(builder, snapshotVersion); builder.add(Constants.QueryConstants.COMPONENT, Constants.QueryConstants.LIST); if (listingContext != null) { diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/file/ShareListHandler.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/file/ShareListHandler.java index 0721d15e7ccdd..3d3b9b99fd657 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/file/ShareListHandler.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/file/ShareListHandler.java @@ -107,7 +107,7 @@ public void endElement(String uri, String localName, String qName) throws SAXExc CloudFileShare retShare = this.serviceClient.getShareReference(this.shareName); retShare.setMetadata(this.attributes.getMetadata()); retShare.setProperties(this.attributes.getProperties()); - //retShare.snapshotID = this.snapshotID; + retShare.snapshotID = this.snapshotID; this.response.getResults().add(retShare); } From 4849bab160b37ac85149402478f2f227639792a6 Mon Sep 17 00:00:00 2001 From: Josh Friedman Date: Thu, 21 Sep 2017 16:06:48 -0700 Subject: [PATCH 04/10] Uncommenting tests for share snapshots --- .../storage/file/CloudFileClientTests.java | 2 +- .../storage/file/CloudFileDirectoryTests.java | 4 ++-- .../storage/file/CloudFileShareTests.java | 23 +++++++------------ .../azure/storage/file/CloudFileTests.java | 4 ++-- .../azure/storage/file/CloudFileClient.java | 2 +- .../azure/storage/file/CloudFileShare.java | 18 +++++++-------- 6 files changed, 23 insertions(+), 30 deletions(-) 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 f83e4d588d3a5..24b890a13ce5e 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 @@ -115,7 +115,7 @@ public void testListSharesMaxResultsValidationTest() throws StorageException, UR assertNotNull(fileClient.listSharesSegmented("thereshouldntbeanyshareswiththisprefix")); } - //@Test + @Test public void testListSharesWithSnapshot() throws StorageException, URISyntaxException { CloudFileClient fileClient = FileTestHelper.createCloudFileClient(); CloudFileShare share = fileClient.getShareReference(UUID.randomUUID().toString()); 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 5da17d5f397d2..7434d425b143a 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 @@ -480,7 +480,7 @@ private static void testMetadataFailures(CloudFileDirectory directory, String ke directory.getMetadata().remove(key); } - //@Test + @Test public void testUnsupportedDirectoryApisWithinShareSnapshot() throws StorageException, URISyntaxException { CloudFileShare snapshot = this.share.createSnapshot(); CloudFileDirectory rootDir = snapshot.getRootDirectoryReference(); @@ -509,7 +509,7 @@ public void testUnsupportedDirectoryApisWithinShareSnapshot() throws StorageExce snapshot.delete(); } - //@Test + @Test public void testSupportedDirectoryApisInShareSnapshot() throws StorageException, URISyntaxException { CloudFileDirectory dir = this.share.getRootDirectoryReference().getDirectoryReference("dir1"); dir.deleteIfExists(); 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 46a84aac4c581..129b9042a961d 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 @@ -494,7 +494,7 @@ public void eventOccurred(SendingRequestEvent eventArg) { assertTrue(this.share.deleteIfExists(null, null, ctx)); } - //@Test + @Test public void testCreateShareSnapshot() throws StorageException, URISyntaxException, IOException { // create share with metadata this.share.create(); @@ -551,15 +551,15 @@ public void testCreateShareSnapshot() throws StorageException, URISyntaxExceptio final UriQueryBuilder uriBuilder = new UriQueryBuilder(); uriBuilder.add("sharesnapshot", snapshot.snapshotID); -// CloudFileShare snapshotRef5 = new CloudFileShare(uriBuilder.addToURI(this.share.getUri()), -// this.share.getServiceClient().getCredentials(), null); -// assertEquals(snapshot.snapshotID, snapshotRef5.snapshotID); -// assertTrue(snapshotRef5.exists()); + CloudFileShare snapshotRef5 = new CloudFileShare(uriBuilder.addToURI(this.share.getUri()), + this.share.getServiceClient().getCredentials()); + assertEquals(snapshot.snapshotID, snapshotRef5.snapshotID); + assertTrue(snapshotRef5.exists()); snapshot.delete(); } - //@Test + @Test public void testDeleteShareSnapshotOptions() throws StorageException, URISyntaxException, IOException { // create share with metadata this.share.create(); @@ -583,7 +583,7 @@ public void testDeleteShareSnapshotOptions() throws StorageException, URISyntaxE assertFalse(snapshot.exists()); } - //@Test + @Test public void testListFilesAndDirectoriesWithinShareSnapshot() throws StorageException, URISyntaxException { this.share.create(); @@ -624,7 +624,7 @@ public void testListFilesAndDirectoriesWithinShareSnapshot() throws StorageExcep snapshot.delete(); } - //@Test + @Test public void testUnsupportedApisShareSnapshot() throws StorageException, URISyntaxException { CloudFileClient client = FileTestHelper.createCloudFileClient(); this.share.create(); @@ -637,13 +637,6 @@ public void testUnsupportedApisShareSnapshot() throws StorageException, URISynta catch (IllegalArgumentException e) { assertEquals(SR.INVALID_OPERATION_FOR_A_SHARE_SNAPSHOT, e.getMessage()); } -// try { -// new CloudFileShare(snapshot.getQualifiedUri(), client.getCredentials(), "2016-10-24T16:37:17.0000000Z"); -// fail("Shouldn't get here"); -// } -// catch (IllegalArgumentException e) { -// assertEquals(SR.SNAPSHOT_QUERY_OPTION_ALREADY_DEFINED, e.getMessage()); -// } try { snapshot.downloadPermissions(); fail("Shouldn't get here"); 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 c2e1d77b787cb..b02a9e2a6ccae 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 @@ -1617,7 +1617,7 @@ private void doCloudFileCopy(boolean sourceIsSas, boolean destinationIsSas) source.delete(); } - //@Test + @Test public void testUnsupportedFileApisWithinShareSnapshot() throws StorageException, URISyntaxException { CloudFileShare snapshot = this.share.createSnapshot(); CloudFile file = snapshot.getRootDirectoryReference().getFileReference("file"); @@ -1677,7 +1677,7 @@ public void testUnsupportedFileApisWithinShareSnapshot() throws StorageException snapshot.delete(); } - //@Test + @Test public void testSupportedFileApisInShareSnapshot() throws StorageException, URISyntaxException, UnsupportedEncodingException { CloudFileDirectory dir = this.share.getRootDirectoryReference().getDirectoryReference("dir1"); dir.deleteIfExists(); diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/file/CloudFileClient.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/file/CloudFileClient.java index 409d4ff2730bf..c113fa53a9653 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/file/CloudFileClient.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/file/CloudFileClient.java @@ -121,7 +121,7 @@ public CloudFileShare getShareReference(final String shareName) throws URISyntax * @see Naming and Referencing Shares, * Directories, Files, and Metadata */ - protected CloudFileShare getShareReference(final String shareName, String snapshotID) throws URISyntaxException, StorageException { + public CloudFileShare getShareReference(final String shareName, String snapshotID) throws URISyntaxException, StorageException { Utility.assertNotNullOrEmpty("shareName", shareName); return new CloudFileShare(shareName, snapshotID, this); } diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/file/CloudFileShare.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/file/CloudFileShare.java index d3d180261010e..5f16b3778164f 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/file/CloudFileShare.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/file/CloudFileShare.java @@ -111,7 +111,7 @@ public final class CloudFileShare { * @see Naming and Referencing Shares, * Directories, Files, and Metadata */ - protected CloudFileShare(final String shareName, String snapshotID, final CloudFileClient client) throws URISyntaxException, + public CloudFileShare(final String shareName, String snapshotID, final CloudFileClient client) throws URISyntaxException, StorageException { Utility.assertNotNull("client", client); Utility.assertNotNull("shareName", shareName); @@ -395,7 +395,7 @@ public void delete(AccessCondition accessCondition, FileRequestOptions options, * If a storage service error occurred. */ @DoesServiceRequest - protected void delete(DeleteShareSnapshotsOption deleteSnapshotsOption, AccessCondition accessCondition, FileRequestOptions options, OperationContext opContext) + public void delete(DeleteShareSnapshotsOption deleteSnapshotsOption, AccessCondition accessCondition, FileRequestOptions options, OperationContext opContext) throws StorageException { if (opContext == null) { opContext = new OperationContext(); @@ -508,7 +508,7 @@ public boolean deleteIfExists(AccessCondition accessCondition, FileRequestOption * If a storage service error occurred. */ @DoesServiceRequest - protected boolean deleteIfExists(DeleteShareSnapshotsOption deleteSnapshotsOption, AccessCondition accessCondition, FileRequestOptions options, + public boolean deleteIfExists(DeleteShareSnapshotsOption deleteSnapshotsOption, AccessCondition accessCondition, FileRequestOptions options, OperationContext opContext) throws StorageException { options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient); @@ -728,7 +728,7 @@ public FileSharePermissions postProcessResponse(HttpURLConnection connection, * If a storage service error occurred. */ @DoesServiceRequest - protected final CloudFileShare createSnapshot() throws StorageException { + public final CloudFileShare createSnapshot() throws StorageException { return this .createSnapshot(null /* metadata */, null /* accessCondition */, null /* options */, null /* opContext */); } @@ -753,7 +753,7 @@ protected final CloudFileShare createSnapshot() throws StorageException { * If a storage service error occurred. */ @DoesServiceRequest - protected final CloudFileShare createSnapshot(final AccessCondition accessCondition, FileRequestOptions options, + public final CloudFileShare createSnapshot(final AccessCondition accessCondition, FileRequestOptions options, OperationContext opContext) throws StorageException { return this.createSnapshot(null /* metadata */, accessCondition, options, opContext); } @@ -780,7 +780,7 @@ protected final CloudFileShare createSnapshot(final AccessCondition accessCondit * If a storage service error occurred. */ @DoesServiceRequest - protected final CloudFileShare createSnapshot(final HashMap metadata, + public final CloudFileShare createSnapshot(final HashMap metadata, final AccessCondition accessCondition, FileRequestOptions options, OperationContext opContext) throws StorageException { assertNoSnapshot(); @@ -1502,7 +1502,7 @@ public URI getUri() { * * @return The snapshotID as a string for this share. */ - protected final String getSnapshot() { + public final String getSnapshot() { return this.snapshotID; } @@ -1513,7 +1513,7 @@ protected final String getSnapshot() { * * @see DeleteSnapshotsOption */ - protected final boolean isSnapshot() { + public final boolean isSnapshot() { return this.snapshotID != null; } @@ -1591,7 +1591,7 @@ public void setMetadata(final HashMap metadata) { * A {@link FileShareProperties} object that represents the properties being assigned to the * share. */ - protected void setProperties(final FileShareProperties properties) { + public void setProperties(final FileShareProperties properties) { this.properties = properties; } From e15f0062d9d081b5f8d45e4c6a8a3af050b734aa Mon Sep 17 00:00:00 2001 From: Josh Friedman Date: Thu, 28 Sep 2017 12:12:38 -0700 Subject: [PATCH 05/10] IOExceptions wrapping StorageExceptions will now contain the StorageException message in the outer exception. --- ChangeLog.txt | 1 + .../azure/storage/EventFiringTests.java | 55 ++++++++++--------- .../microsoft/azure/storage/core/Utility.java | 11 +++- 3 files changed, 39 insertions(+), 28 deletions(-) diff --git a/ChangeLog.txt b/ChangeLog.txt index 5c229d60c1a64..d7396d95c8d11 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -1,5 +1,6 @@ 2017.XX.XX Version 6.0.0 * Added support for taking a snapshot of a share. + * IOExceptions wrapping StorageExceptions will now contain the StorageException message in the outer exception. 2017.08.28 Version 5.5.0 * Fixed a bug when using a SAS token where the token was being omitted from calls to delete a directory in the file service. 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 6ee2236d86e86..b42ae25e8d938 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 @@ -20,14 +20,10 @@ import com.microsoft.azure.storage.TestRunners.DevFabricTests; import com.microsoft.azure.storage.TestRunners.DevStoreTests; -import org.apache.http.protocol.HTTP; import org.junit.Test; import org.junit.experimental.categories.Category; -import java.io.ByteArrayInputStream; -import java.io.IOException; import java.net.HttpURLConnection; -import java.net.SocketException; import java.net.URISyntaxException; import java.util.ArrayList; @@ -121,39 +117,46 @@ public void eventOccurred(ErrorReceivingResponseEvent eventArg) { } }); - OperationContext.getGlobalErrorReceivingResponseEventHandler().addListener(new StorageEvent() { + StorageEvent globalResponseReceivedListener = new StorageEvent() { @Override public void eventOccurred(ErrorReceivingResponseEvent eventArg) { fail("This event should not trigger"); } - }); + }; - assertEquals(0, callList.size()); - assertEquals(0, globalCallList.size()); + try { + OperationContext.getGlobalErrorReceivingResponseEventHandler().addListener(globalResponseReceivedListener); - CloudBlobClient blobClient = TestHelper.createCloudBlobClient(); - CloudBlobContainer container = blobClient.getContainerReference("container1"); + assertEquals(0, callList.size()); + assertEquals(0, globalCallList.size()); - // make sure both update - container.exists(null, null, eventContext); - assertEquals(1, callList.size()); - assertEquals(1, globalCallList.size()); + CloudBlobClient blobClient = TestHelper.createCloudBlobClient(); + CloudBlobContainer container = blobClient.getContainerReference("container1"); - // make sure only global updates - container.exists(); - assertEquals(1, callList.size()); - assertEquals(2, globalCallList.size()); + // make sure both update + container.exists(null, null, eventContext); + assertEquals(1, callList.size()); + assertEquals(1, globalCallList.size()); - OperationContext - .setGlobalResponseReceivedEventHandler(new StorageEventMultiCaster>()); - eventContext - .setResponseReceivedEventHandler(new StorageEventMultiCaster>()); + // make sure only global updates + container.exists(); + assertEquals(1, callList.size()); + assertEquals(2, globalCallList.size()); - // make sure neither update - container.exists(null, null, eventContext); - assertEquals(1, callList.size()); - assertEquals(2, globalCallList.size()); + OperationContext + .setGlobalResponseReceivedEventHandler(new StorageEventMultiCaster>()); + eventContext + .setResponseReceivedEventHandler(new StorageEventMultiCaster>()); + + // make sure neither update + container.exists(null, null, eventContext); + assertEquals(1, callList.size()); + assertEquals(2, globalCallList.size()); + } + finally { + OperationContext.getGlobalErrorReceivingResponseEventHandler().removeListener(globalResponseReceivedListener); + } } @Test 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 731d55971b68e..8facaed5fc900 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 @@ -767,8 +767,15 @@ public static XMLStreamWriter createXMLStreamWriter(StringWriter outWriter) thro * @return A java.io.IOException object that represents the created IO exception. */ public static IOException initIOException(final Exception ex) { - final IOException retEx = new IOException(); - retEx.initCause(ex); + String message = null; + if (ex != null && ex.getMessage() != null) { + message = ex.getMessage() + " Please see the cause for further information."; + } + else { + message = "Please see the cause for further information."; + } + + final IOException retEx = new IOException(message, ex); return retEx; } From 5dc930892f1ab0a645bc4225b845dd9a7320806a Mon Sep 17 00:00:00 2001 From: Josh Friedman Date: Thu, 5 Oct 2017 16:35:41 -0700 Subject: [PATCH 06/10] Uncommenting setting metadata and properties on exist calls for shares and directories --- .../azure/storage/file/CloudFileDirectoryTests.java | 2 +- .../azure/storage/file/CloudFileShareTests.java | 2 +- .../microsoft/azure/storage/file/CloudFileTests.java | 2 +- .../azure/storage/file/CloudFileDirectory.java | 10 +++++----- .../microsoft/azure/storage/file/CloudFileShare.java | 8 ++++---- 5 files changed, 12 insertions(+), 12 deletions(-) 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 7434d425b143a..92de13ae410f3 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 @@ -60,7 +60,7 @@ public void cloudFileDirectorySetUp() throws URISyntaxException, StorageExceptio @After public void cloudFileDirectoryTearDown() throws StorageException { - this.share.deleteIfExists(); + this.share.deleteIfExists(DeleteShareSnapshotsOption.INCLUDE_SNAPSHOTS, null, null, null); } /** 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 129b9042a961d..87b73dfc30db6 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 @@ -62,7 +62,7 @@ public void fileShareTestMethodSetUp() throws StorageException, URISyntaxExcepti @After public void fileShareTestMethodTearDown() throws StorageException { - this.share.deleteIfExists(); + this.share.deleteIfExists(DeleteShareSnapshotsOption.INCLUDE_SNAPSHOTS, null, null, null); } /** 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 b02a9e2a6ccae..b12ec11dcb1d3 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 @@ -86,7 +86,7 @@ public void fileTestMethodSetUp() throws URISyntaxException, StorageException { @After public void fileTestMethodTearDown() throws StorageException { - this.share.deleteIfExists(); + this.share.deleteIfExists(DeleteShareSnapshotsOption.INCLUDE_SNAPSHOTS, null, null, null); } /** diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/file/CloudFileDirectory.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/file/CloudFileDirectory.java index 9eaae67ca2ba5..b671958cee783 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/file/CloudFileDirectory.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/file/CloudFileDirectory.java @@ -532,11 +532,11 @@ public Boolean preProcessResponse(CloudFileDirectory directory, CloudFileClient OperationContext context) throws Exception { if (this.getResult().getStatusCode() == HttpURLConnection.HTTP_OK) { directory.updatePropertiesFromResponse(this.getConnection()); -// // Set properties -// final FileDirectoryAttributes attributes = -// FileResponse.getFileDirectoryAttributes(this.getConnection(), client.isUsePathStyleUris()); -// directory.setMetadata(attributes.getMetadata()); -// directory.setProperties(attributes.getProperties()); + // Set properties + final FileDirectoryAttributes attributes = + FileResponse.getFileDirectoryAttributes(this.getConnection(), client.isUsePathStyleUris()); + directory.setMetadata(attributes.getMetadata()); + directory.setProperties(attributes.getProperties()); return Boolean.valueOf(true); } diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/file/CloudFileShare.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/file/CloudFileShare.java index 5f16b3778164f..1790b5a1afde3 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/file/CloudFileShare.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/file/CloudFileShare.java @@ -1020,10 +1020,10 @@ public Boolean preProcessResponse(CloudFileShare share, CloudFileClient client, throws Exception { if (this.getResult().getStatusCode() == HttpURLConnection.HTTP_OK) { share.updatePropertiesFromResponse(this.getConnection()); -// final FileShareAttributes attributes = FileResponse.getFileShareAttributes(this.getConnection(), -// client.isUsePathStyleUris()); -// share.metadata = attributes.getMetadata(); -// share.properties = attributes.getProperties(); + final FileShareAttributes attributes = FileResponse.getFileShareAttributes(this.getConnection(), + client.isUsePathStyleUris()); + share.metadata = attributes.getMetadata(); + share.properties = attributes.getProperties(); return Boolean.valueOf(true); } From a88a09ac2b82c748a5e54960d2d09475382fdfb0 Mon Sep 17 00:00:00 2001 From: Josh Friedman Date: Thu, 5 Oct 2017 16:38:46 -0700 Subject: [PATCH 07/10] Updating version for 6.0.0 release --- README.md | 2 +- microsoft-azure-storage-samples/pom.xml | 2 +- .../src/com/microsoft/azure/storage/logging/pom.xml | 2 +- .../src/com/microsoft/azure/storage/Constants.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index f0bff9df4c2ed..df9acf4fa0a06 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ To get the binaries of this library as distributed by Microsoft, ready for use w com.microsoft.azure azure-storage - 5.5.0 + 6.0.0 ``` diff --git a/microsoft-azure-storage-samples/pom.xml b/microsoft-azure-storage-samples/pom.xml index e1ddefbd90ec0..c697eb4e8dd54 100644 --- a/microsoft-azure-storage-samples/pom.xml +++ b/microsoft-azure-storage-samples/pom.xml @@ -26,7 +26,7 @@ com.microsoft.azure azure-storage - 5.5.0 + 6.0.0 com.microsoft.azure diff --git a/microsoft-azure-storage-samples/src/com/microsoft/azure/storage/logging/pom.xml b/microsoft-azure-storage-samples/src/com/microsoft/azure/storage/logging/pom.xml index b129134f42437..f5c5d85d20a3c 100644 --- a/microsoft-azure-storage-samples/src/com/microsoft/azure/storage/logging/pom.xml +++ b/microsoft-azure-storage-samples/src/com/microsoft/azure/storage/logging/pom.xml @@ -26,7 +26,7 @@ com.microsoft.azure azure-storage - 5.5.0 + 6.0.0 com.microsoft.azure 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 ccc010d05b6b8..69d063316a2b8 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/Constants.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/Constants.java @@ -661,7 +661,7 @@ public static class HeaderConstants { /** * Specifies the value to use for UserAgent header. */ - public static final String USER_AGENT_VERSION = "5.5.0"; + public static final String USER_AGENT_VERSION = "6.0.0"; /** * The default type for content-type and accept From df95f5b68db74f8c1fa4517334db36973299f09d Mon Sep 17 00:00:00 2001 From: Josh Friedman Date: Fri, 6 Oct 2017 09:46:36 -0700 Subject: [PATCH 08/10] Updating breaking changes for 6.0.0 release --- BreakingChanges.txt | 5 +---- ChangeLog.txt | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/BreakingChanges.txt b/BreakingChanges.txt index 618c0ad387a82..2cff35062f9ad 100644 --- a/BreakingChanges.txt +++ b/BreakingChanges.txt @@ -1,10 +1,7 @@ Changes in 6.0.0 FILE -* CloudFileShare constructor now takes a snapshotID String which can be null. - -* FileRequest get methods now takes a snapshotID String which can be null. - +* 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. Changes in 5.1.1 diff --git a/ChangeLog.txt b/ChangeLog.txt index d7396d95c8d11..fcafcfefa0fb2 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -1,4 +1,4 @@ -2017.XX.XX Version 6.0.0 +2017.10.06 Version 6.0.0 * Added support for taking a snapshot of a share. * IOExceptions wrapping StorageExceptions will now contain the StorageException message in the outer exception. From bfb24a4a2b17693454ac8d400f615b658c581332 Mon Sep 17 00:00:00 2001 From: Josh Friedman Date: Fri, 6 Oct 2017 14:30:50 -0700 Subject: [PATCH 09/10] Fixing share listing for snapshots --- .../storage/file/CloudFileClientTests.java | 78 ++++++++++--------- .../storage/file/CloudFileShareTests.java | 2 +- .../azure/storage/file/CloudFileClient.java | 30 +++---- .../azure/storage/file/FileRequest.java | 43 +++++----- .../storage/file/ShareListingDetails.java | 9 +-- 5 files changed, 81 insertions(+), 81 deletions(-) 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 24b890a13ce5e..17b38132224f6 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 @@ -67,7 +67,7 @@ public void testListSharesTest() throws StorageException, URISyntaxException { do { ResultSegment segment = fileClient.listSharesSegmented(prefix, - ShareListingDetails.ALL, 15, token, null, null); + EnumSet.allOf(ShareListingDetails.class), 15, token, null, null); for (final CloudFileShare share : segment.getResults()) { share.downloadAttributes(); @@ -104,7 +104,7 @@ public void testListSharesMaxResultsValidationTest() throws StorageException, UR for (int i = 0; i >= -2; i--) { try{ fileClient.listSharesSegmented( - prefix, ShareListingDetails.ALL, i, null, null, null); + prefix, EnumSet.allOf(ShareListingDetails.class), i, null, null, null); fail(); } catch (IllegalArgumentException e) { @@ -119,42 +119,46 @@ public void testListSharesMaxResultsValidationTest() throws StorageException, UR public void testListSharesWithSnapshot() throws StorageException, URISyntaxException { CloudFileClient fileClient = FileTestHelper.createCloudFileClient(); CloudFileShare share = fileClient.getShareReference(UUID.randomUUID().toString()); - share.create(); - - HashMap shareMeta = new HashMap(); - shareMeta.put("key1", "value1"); - share.setMetadata(shareMeta); - share.uploadMetadata(); - - CloudFileShare snapshot = share.createSnapshot(); - HashMap meta2 = new HashMap(); - meta2.put("key2", "value2"); - share.setMetadata(meta2); - share.uploadMetadata(); - - CloudFileClient client = FileTestHelper.createCloudFileClient(); - Iterable listResult = client.listShares(share.name, ShareListingDetails.ALL, null, null); - - int count = 0; - boolean originalFound = false; - boolean snapshotFound = false; - for (CloudFileShare listShareItem : listResult) { - if (listShareItem.getName().equals(share.getName()) && !listShareItem.isSnapshot() && !originalFound) - { - count++; - originalFound = true; - assertEquals(share.getMetadata(), listShareItem.getMetadata()); - assertEquals(share.getStorageUri(), listShareItem.getStorageUri()); - } - else if (listShareItem.getName().equals(share.getName()) && - listShareItem.isSnapshot() && !snapshotFound) { - count++; - snapshotFound = true; - assertEquals(snapshot.getMetadata(), listShareItem.getMetadata()); - assertEquals(snapshot.getStorageUri(), listShareItem.getStorageUri()); + try { + share.create(); + + HashMap shareMeta = new HashMap(); + shareMeta.put("key1", "value1"); + share.setMetadata(shareMeta); + share.uploadMetadata(); + + CloudFileShare snapshot = share.createSnapshot(); + HashMap meta2 = new HashMap(); + meta2.put("key2", "value2"); + share.setMetadata(meta2); + share.uploadMetadata(); + + CloudFileClient client = FileTestHelper.createCloudFileClient(); + Iterable listResult = client.listShares(share.name, EnumSet.allOf(ShareListingDetails.class), null, null); + + int count = 0; + boolean originalFound = false; + boolean snapshotFound = false; + for (CloudFileShare listShareItem : listResult) { + if (listShareItem.getName().equals(share.getName()) && !listShareItem.isSnapshot() && !originalFound) { + count++; + originalFound = true; + assertEquals(share.getMetadata(), listShareItem.getMetadata()); + assertEquals(share.getStorageUri(), listShareItem.getStorageUri()); + } else if (listShareItem.getName().equals(share.getName()) && + listShareItem.isSnapshot() && !snapshotFound) { + count++; + snapshotFound = true; + assertEquals(snapshot.getMetadata(), listShareItem.getMetadata()); + assertEquals(snapshot.getStorageUri(), listShareItem.getStorageUri()); + } } - } - assertEquals(2, count); + assertEquals(2, count); + } + finally + { + share.deleteIfExists(DeleteShareSnapshotsOption.INCLUDE_SNAPSHOTS, null, null, null); + } } } \ No newline at end of file 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 87b73dfc30db6..1153923e4add1 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 @@ -318,7 +318,7 @@ public void testCloudFileShareUploadMetadata() throws StorageException, URISynta Assert.assertEquals("value2", this.share.getMetadata().get("key2")); Iterable shares = this.share.getServiceClient().listShares(this.share.getName(), - ShareListingDetails.METADATA, null, null); + EnumSet.of(ShareListingDetails.METADATA), null, null); for (CloudFileShare share3 : shares) { Assert.assertEquals(2, share3.getMetadata().size()); diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/file/CloudFileClient.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/file/CloudFileClient.java index c113fa53a9653..078c00849dcc8 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/file/CloudFileClient.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/file/CloudFileClient.java @@ -134,7 +134,7 @@ public CloudFileShare getShareReference(final String shareName, String snapshotI */ @DoesServiceRequest public Iterable listShares() { - return this.listSharesWithPrefix(null, ShareListingDetails.NONE, null /* options */, null /* opContext */); + return this.listSharesWithPrefix(null, EnumSet.noneOf(ShareListingDetails.class), null /* options */, null /* opContext */); } /** @@ -149,7 +149,7 @@ public Iterable listShares() { */ @DoesServiceRequest public Iterable listShares(final String prefix) { - return this.listSharesWithPrefix(prefix, ShareListingDetails.NONE, null /* options */, null /* opContext */); + return this.listSharesWithPrefix(prefix, EnumSet.noneOf(ShareListingDetails.class), null /* options */, null /* opContext */); } /** @@ -159,7 +159,8 @@ public Iterable listShares(final String prefix) { * @param prefix * A String that represents the share name prefix. * @param detailsIncluded - * A {@link ShareListingDetails} value that indicates whether share metadata will be returned. + * A java.util.EnumSet object that contains {@link ShareListingDetails} values that indicate + * whether share snapshots and/or metadata will be returned. * @param options * A {@link FileRequestOptions} object that specifies any additional options for the request. Specifying * null will use the default request options from the associated service client ( @@ -173,7 +174,7 @@ public Iterable listShares(final String prefix) { * shares for this client. */ @DoesServiceRequest - public Iterable listShares(final String prefix, final ShareListingDetails detailsIncluded, + public Iterable listShares(final String prefix, final EnumSet detailsIncluded, final FileRequestOptions options, final OperationContext opContext) { return this.listSharesWithPrefix(prefix, detailsIncluded, options, opContext); } @@ -189,7 +190,7 @@ public Iterable listShares(final String prefix, final ShareListi */ @DoesServiceRequest public ResultSegment listSharesSegmented() throws StorageException { - return this.listSharesSegmented(null, ShareListingDetails.NONE, null, null /* continuationToken */, + return this.listSharesSegmented(null, EnumSet.noneOf(ShareListingDetails.class), null, null /* continuationToken */, null /* options */, null /* opContext */); } @@ -209,7 +210,7 @@ public ResultSegment listSharesSegmented() throws StorageExcepti */ @DoesServiceRequest public ResultSegment listSharesSegmented(final String prefix) throws StorageException { - return this.listSharesWithPrefixSegmented(prefix, ShareListingDetails.NONE, null, null /* continuationToken */, + return this.listSharesWithPrefixSegmented(prefix, EnumSet.noneOf(ShareListingDetails.class), null, null /* continuationToken */, null /* options */, null /* opContext */); } @@ -220,7 +221,8 @@ public ResultSegment listSharesSegmented(final String prefix) th * @param prefix * A String that represents the prefix of the share name. * @param detailsIncluded - * A {@link ShareListingDetails} value that indicates whether share metadata will be returned. + * A java.util.EnumSet object that contains {@link ShareListingDetails} values that indicate + * whether share snapshots and/or metadata will be returned. * @param maxResults * The maximum number of results to retrieve. If null or greater * than 5000, the server will return up to 5,000 items. Must be at least 1. @@ -244,7 +246,7 @@ public ResultSegment listSharesSegmented(final String prefix) th */ @DoesServiceRequest public ResultSegment listSharesSegmented(final String prefix, - final ShareListingDetails detailsIncluded, final Integer maxResults, + final EnumSet detailsIncluded, final Integer maxResults, final ResultContinuation continuationToken, final FileRequestOptions options, final OperationContext opContext) throws StorageException { return this.listSharesWithPrefixSegmented(prefix, detailsIncluded, maxResults, continuationToken, options, @@ -258,7 +260,8 @@ public ResultSegment listSharesSegmented(final String prefix, * @param prefix * A String that represents the prefix of the share name. * @param detailsIncluded - * A {@link ShareListingDetails} value that indicates whether share metadata will be returned. + * A java.util.EnumSet object that contains {@link ShareListingDetails} values that indicate + * whether share snapshots and/or metadata will be returned. * @param options * A {@link FileRequestOptions} object that specifies any additional options for the request. Specifying * null will use the default request options from the associated service client ( @@ -272,7 +275,7 @@ public ResultSegment listSharesSegmented(final String prefix, * shares whose names begin with the specified prefix. */ private Iterable listSharesWithPrefix(final String prefix, - final ShareListingDetails detailsIncluded, FileRequestOptions options, OperationContext opContext) { + final EnumSet detailsIncluded, FileRequestOptions options, OperationContext opContext) { if (opContext == null) { opContext = new OperationContext(); } @@ -294,7 +297,8 @@ private Iterable listSharesWithPrefix(final String prefix, * @param prefix * A String that represents the prefix of the share name. * @param detailsIncluded - * A {@link ShareListingDetails} value that indicates whether share metadata will be returned. + * A java.util.EnumSet object that contains {@link ShareListingDetails} values that indicate + * whether share snapshots and/or metadata will be returned. * @param maxResults * The maximum number of results to retrieve. If null or greater * than 5000, the server will return up to 5,000 items. Must be at least 1. @@ -317,7 +321,7 @@ private Iterable listSharesWithPrefix(final String prefix, * If a storage service error occurred. */ private ResultSegment listSharesWithPrefixSegmented(final String prefix, - final ShareListingDetails detailsIncluded, final Integer maxResults, + final EnumSet detailsIncluded, final Integer maxResults, final ResultContinuation continuationToken, FileRequestOptions options, OperationContext opContext) throws StorageException { if (opContext == null) { @@ -338,7 +342,7 @@ private ResultSegment listSharesWithPrefixSegmented(final String } private StorageRequest> listSharesWithPrefixSegmentedImpl( - final String prefix, final ShareListingDetails detailsIncluded, final Integer maxResults, + final String prefix, final EnumSet detailsIncluded, final Integer maxResults, final FileRequestOptions options, final SegmentedStorageRequest segmentedRequest) { Utility.assertContinuationType(segmentedRequest.getToken(), ResultContinuationType.SHARE); diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/file/FileRequest.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/file/FileRequest.java index 092f4866a6d62..49c5f33c439c7 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/file/FileRequest.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/file/FileRequest.java @@ -640,7 +640,8 @@ private static HttpURLConnection getProperties(final URI uri, final FileRequestO * @param listingContext * A set of parameters for the listing operation. * @param detailsIncluded - * Additional details to return with the listing. + * A java.util.EnumSet object that contains {@link ShareListingDetails} values that indicate + * whether share snapshots and/or metadata will be returned. * @return a HttpURLConnection configured for the operation. * @throws IOException * @throws URISyntaxException @@ -649,32 +650,28 @@ private static HttpURLConnection getProperties(final URI uri, final FileRequestO */ public static HttpURLConnection listShares(final URI uri, final FileRequestOptions fileOptions, final OperationContext opContext, final ListingContext listingContext, - final ShareListingDetails detailsIncluded) throws URISyntaxException, IOException, StorageException { + final EnumSet detailsIncluded) throws URISyntaxException, IOException, StorageException { final UriQueryBuilder builder = BaseRequest.getListUriQueryBuilder(listingContext); -// if (detailsIncluded != null && detailsIncluded.size() > 0) { -// final StringBuilder sb = new StringBuilder(); -// boolean started = false; + if (detailsIncluded != null && detailsIncluded.size() > 0) { + final StringBuilder sb = new StringBuilder(); + boolean started = false; -// if (detailsIncluded.contains(ShareListingDetails.SNAPSHOTS)) { -// started = true; -// sb.append(SNAPSHOTS_QUERY_ELEMENT_NAME); -// } + if (detailsIncluded.contains(ShareListingDetails.SNAPSHOTS)) { + started = true; + sb.append(SNAPSHOTS_QUERY_ELEMENT_NAME); + } -// if (detailsIncluded.contains(ShareListingDetails.METADATA)) { -// if (started) -// { -// sb.append(","); -// } -// -// sb.append(Constants.QueryConstants.METADATA); -// } - -// builder.add(Constants.QueryConstants.INCLUDE, sb.toString()); -// } - - if (detailsIncluded == ShareListingDetails.ALL || detailsIncluded == ShareListingDetails.METADATA) { - builder.add(Constants.QueryConstants.INCLUDE, Constants.QueryConstants.METADATA); + if (detailsIncluded.contains(ShareListingDetails.METADATA)) { + if (started) + { + sb.append(","); + } + + sb.append(Constants.QueryConstants.METADATA); + } + + builder.add(Constants.QueryConstants.INCLUDE, sb.toString()); } final HttpURLConnection request = BaseRequest.createURLConnection(uri, fileOptions, builder, opContext); diff --git a/microsoft-azure-storage/src/com/microsoft/azure/storage/file/ShareListingDetails.java b/microsoft-azure-storage/src/com/microsoft/azure/storage/file/ShareListingDetails.java index 87640da8982eb..14bb2c0753ec6 100644 --- a/microsoft-azure-storage/src/com/microsoft/azure/storage/file/ShareListingDetails.java +++ b/microsoft-azure-storage/src/com/microsoft/azure/storage/file/ShareListingDetails.java @@ -24,20 +24,15 @@ public enum ShareListingDetails { */ NONE(0), - /** - * Specifies including all available details. - */ - ALL(1), - /** * Specifies including share metadata. */ - METADATA(1); + METADATA(1), /** * Specifies listing share snapshots. */ - //SNAPSHOTS(2); + SNAPSHOTS(2); /** * Returns the value of this enum. From 23a1b4e4fc8ff69b38ab1744b2b87e768a6688d4 Mon Sep 17 00:00:00 2001 From: Josh Friedman Date: Fri, 6 Oct 2017 14:41:51 -0700 Subject: [PATCH 10/10] Updating main pom --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9d5c3231d80fb..8aa563d667a22 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ 4.0.0 com.microsoft.azure azure-storage - 5.5.0 + 6.0.0 jar Microsoft Azure Storage Client SDK