/** * Copyright Microsoft Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.microsoft.azure.storage.table; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URI; import java.net.URISyntaxException; import java.util.Date; import java.util.HashMap; import com.microsoft.azure.storage.DoesServiceRequest; import com.microsoft.azure.storage.LocationMode; import com.microsoft.azure.storage.OperationContext; import com.microsoft.azure.storage.ResultContinuation; import com.microsoft.azure.storage.ResultContinuationType; import com.microsoft.azure.storage.ResultSegment; import com.microsoft.azure.storage.RetryExponentialRetry; import com.microsoft.azure.storage.ServiceClient; import com.microsoft.azure.storage.ServiceProperties; import com.microsoft.azure.storage.ServiceStats; import com.microsoft.azure.storage.StorageCredentials; import com.microsoft.azure.storage.StorageException; import com.microsoft.azure.storage.StorageUri; import com.microsoft.azure.storage.blob.BlobRequestOptions; import com.microsoft.azure.storage.blob.CloudBlobClient; import com.microsoft.azure.storage.core.ExecutionEngine; import com.microsoft.azure.storage.core.LazySegmentedIterable; import com.microsoft.azure.storage.core.SR; import com.microsoft.azure.storage.core.SegmentedStorageRequest; import com.microsoft.azure.storage.core.StorageRequest; import com.microsoft.azure.storage.core.Utility; /** * Provides a service client for accessing the Microsoft Azure Table service. * <p> * The {@link CloudTableClient} class encapsulates the base URI for the Table service endpoint and the credentials for * accessing the storage account, and provides methods to create, delete, list, and query tables, as well as methods to * execute operations and queries on table entities. These methods invoke Storage Service REST API operations to make * the requests and obtain the results that are returned. * <p> * A Table service endpoint is the base URI for Table service resources, including the DNS name of the storage account: * <br> * <code>    http://<em>myaccount</em>.table.core.windows.net</code><br> * For more information, see the MSDN topic <a * href="http://msdn.microsoft.com/en-us/library/azure/dd179360.aspx">Addressing Table Service Resources</a>. * <p> * The credentials can be a combination of the storage account name and a key, or a shared access signature. For more * information, see the MSDN topic <a href="http://msdn.microsoft.com/en-us/library/azure/hh225339.aspx">Authenticating * Access to Your Storage Account</a>. * */ public final class CloudTableClient extends ServiceClient { /** * Holds the default request option values associated with this Service Client. */ private TableRequestOptions defaultRequestOptions; /** * Reserved for internal use. An {@link EntityResolver} that projects table entity data as a <code>String</code> * containing the table name. */ private final EntityResolver<String> tableNameResolver = new EntityResolver<String>() { @Override public String resolve(String partitionKey, String rowKey, Date timeStamp, HashMap<String, EntityProperty> properties, String etag) { return properties.get(TableConstants.TABLE_NAME).getValueAsString(); } }; /** * Initializes an instance of the <code>CloudTableClient</code> class using a Table service endpoint and * storage account credentials. * * @param baseUri * A <code>java.net.URI</code> object that represents the Table service endpoint used to initialize the * client. * @param credentials * A {@link StorageCredentials} object that represents the storage account credentials for access. */ public CloudTableClient(final URI baseUri, StorageCredentials credentials) { this(new StorageUri(baseUri), credentials); } /** * Initializes an instance of the <code>CloudTableClient</code>class using a Table service endpoint and * storage account credentials. * * @param baseUri * A {@link StorageUri} object that represents the Table service endpoint used to initialize the * client. * @param credentials * A {@link StorageCredentials} object that represents the storage account credentials for access. */ public CloudTableClient(final StorageUri baseUri, StorageCredentials credentials) { super(baseUri, credentials); if (credentials == null) { throw new IllegalArgumentException(SR.STORAGE_TABLE_CREDENTIALS_NULL); } this.defaultRequestOptions = new TableRequestOptions(); this.defaultRequestOptions.setLocationMode(LocationMode.PRIMARY_ONLY); this.defaultRequestOptions.setRetryPolicyFactory(new RetryExponentialRetry()); this.defaultRequestOptions.setTablePayloadFormat(TablePayloadFormat.Json); } /** * Gets a {@link CloudTable} object with the specified name. * * @param tableName * A <code>String</code> which represents the name of the table, which must adhere to table naming rules. * The table name should not include any path separator characters (/). * Table names are case insensitive, must be unique within an account and must be between 3-63 characters * long. Table names must start with an cannot begin with a numeric character and may only contain * alphanumeric characters. Some table names are reserved, including "table". * * @return A reference to a {@link CloudTable} object. * * @throws URISyntaxException * If the resource URI constructed based on the tableName is invalid. * @throws StorageException * If a storage service error occurred. * @see <a href="http://msdn.microsoft.com/en-us/library/azure/dd179338.aspx">Understanding the Table Service Data * Model</a> */ public CloudTable getTableReference(final String tableName) throws URISyntaxException, StorageException { Utility.assertNotNullOrEmpty("tableName", tableName); return new CloudTable(tableName, this); } /** * Lists the table names in the storage account. * <p> * This method invokes the <a href="http://msdn.microsoft.com/en-us/library/azure/dd179405.aspx">Query Tables</a> * REST API to list the table names, using the Table service endpoint and storage account credentials of this * instance. * * @return An <code>Iterable</code> collection of the table names in the storage account retrieved lazily. */ @DoesServiceRequest public Iterable<String> listTables() { return this.listTables(null); } /** * Lists the table names in the storage account that match the specified prefix. * <p> * This method invokes the <a href="http://msdn.microsoft.com/en-us/library/azure/dd179405.aspx">Query Tables</a> * REST API to list the table names that match the prefix, using the Table service endpoint and storage account * credentials of this instance. * * @param prefix * A <code>String</code> containing the prefix to match on table names to return. * * @return * An <code>Iterable</code> collection of the table names in the storage account retrieved lazily that match * the specified prefix. */ @DoesServiceRequest public Iterable<String> listTables(final String prefix) { return this.listTables(prefix, null /* options */, null /* opContext */); } /** * Lists the table names in the storage account that match the specified prefix, using the specified * {@link TableRequestOptions} and {@link OperationContext}. * <p> * This method invokes the <a href="http://msdn.microsoft.com/en-us/library/azure/dd179405.aspx">Query Tables</a> * REST API to list the table names that match the prefix, using the Table service endpoint and storage account * credentials of this instance. * * Use the {@link TableRequestOptions} to override execution options such as the timeout or retry policy for the * operation. * * @param prefix * A <code>String</code> containing the prefix to match on table names to return. * @param options * A {@link TableRequestOptions} object that specifies execution options such as retry policy and timeout * settings for the operation. Specify <code>null</code> to use the request options specified on the * {@link CloudTableClient}. * @param opContext * An {@link OperationContext} object for tracking the current operation. Specify <code>null</code> to * safely ignore operation context. * * @return * An <code>Iterable</code> collection of the table names in the storage account retrieved lazily that match * the specified * prefix. */ @SuppressWarnings("unchecked") @DoesServiceRequest public Iterable<String> listTables(final String prefix, final TableRequestOptions options, final OperationContext opContext) { return (Iterable<String>) this.generateIteratorForQuery(this.generateListTablesQuery(prefix), this.tableNameResolver, options, opContext); } /** * Lists the table names in the storage account in segmented mode. This method allows listing of tables to be * resumed after returning a partial set of results, using information returned by the server in the * {@link ResultSegment} object. * <p> * This method invokes the <a href="http://msdn.microsoft.com/en-us/library/azure/dd179405.aspx">Query Tables</a> * REST API to list the table names, using the Table service endpoint and storage account credentials of this * instance. * * @return * A {@link ResultSegment} of <code>String</code> objects containing table names in the storage account. * * @throws StorageException * if a storage service error occurred during the operation. */ @DoesServiceRequest public ResultSegment<String> listTablesSegmented() throws StorageException { return this.listTablesSegmented(null); } /** * Lists the table names in the storage account that match the specified prefix in segmented mode. This method * allows listing of tables to be resumed after returning a partial set of results, using information returned by * the server in the {@link ResultSegment} object. * <p> * This method invokes the <a href="http://msdn.microsoft.com/en-us/library/azure/dd179405.aspx">Query Tables</a> * REST API to list the table names that match the prefix, using the Table service endpoint and storage account * credentials of this instance. * * @param prefix * A <code>String</code> containing the prefix to match on table names to return. * * @return * A {@link ResultSegment} of <code>String</code> objects containing table names matching the prefix in the * storage account. * * @throws StorageException * if a storage service error occurred during the operation. */ @DoesServiceRequest public ResultSegment<String> listTablesSegmented(final String prefix) throws StorageException { return this.listTablesSegmented(prefix, null /* maxResults */, null /* continuationToken */, null /* options */, null /* opContext */); } /** * Lists up to the specified maximum of the table names in the storage account that match the specified prefix in a * resumable mode with the specified {@link ResultContinuation} continuation token, using the specified * {@link TableRequestOptions} and {@link OperationContext}. This method allows listing of tables to be resumed * after returning a page of results, using information returned by the server in the {@link ResultSegment} object. * <p> * This method invokes the <a href="http://msdn.microsoft.com/en-us/library/azure/dd179405.aspx">Query Tables</a> * REST API to list the table names that match the prefix, using the Table service endpoint and storage account * credentials of this instance. * * Use the {@link TableRequestOptions} to override execution options such as the timeout or retry policy for the * operation. * * @param prefix * A <code>String</code> containing the prefix to match on table names to return. * @param maxResults * The maximum number of table names to return in the {@link ResultSegment}. If this parameter is null, * the query will list up to the maximum 1,000 results. * @param continuationToken * A {@link ResultContinuation} object representing a continuation token from the server when the * operation returns a partial result. Specify <code>null</code> on the initial call. Call the * {@link ResultSegment#getContinuationToken()} method on the result to obtain the * {@link ResultContinuation} object to use in the next call to resume the query. * @param options * A {@link TableRequestOptions} object that specifies execution options such as retry policy and timeout * settings for the operation. Specify <code>null</code> to use the request options specified on the * {@link CloudTableClient}. * @param opContext * An {@link OperationContext} object for tracking the current operation. Specify <code>null</code> to * safely ignore operation context. * * @return * A {@link ResultSegment} of <code>String</code> objects containing table names in the storage account. * * @throws StorageException * if a storage service error occurred during the operation. */ @SuppressWarnings("unchecked") @DoesServiceRequest public ResultSegment<String> listTablesSegmented(final String prefix, final Integer maxResults, final ResultContinuation continuationToken, final TableRequestOptions options, final OperationContext opContext) throws StorageException { return (ResultSegment<String>) this.executeQuerySegmentedImpl( this.generateListTablesQuery(prefix).take(maxResults), this.tableNameResolver, continuationToken, options, opContext); } /** * Reserved for internal use. Generates a query to list table names with the given prefix. * * @param prefix * A <code>String</code> containing the prefix to match on table names to return. * @return * A {@link TableQuery} instance for listing table names with the specified prefix. */ private TableQuery<TableServiceEntity> generateListTablesQuery(final String prefix) { TableQuery<TableServiceEntity> listQuery = TableQuery.<TableServiceEntity> from(TableServiceEntity.class); listQuery.setSourceTableName(TableConstants.TABLES_SERVICE_TABLES_NAME); if (!Utility.isNullOrEmpty(prefix)) { // Append Max char to end '{' is 1 + 'z' in AsciiTable > uppperBound = prefix + '{' final String prefixFilter = String.format("(%s ge '%s') and (%s lt '%s{')", TableConstants.TABLE_NAME, prefix, TableConstants.TABLE_NAME, prefix); listQuery = listQuery.where(prefixFilter); } return listQuery; } /** * Reserved for internal use. Executes a segmented query operation using the specified retry and timeout policies. * * @param queryToExecute * The {@link TableQuery} to execute. * @param resolver * An {@link EntityResolver} instance which creates a projection of the table query result entities into * the specified type <code>R</code>. Pass <code>null</code> to return the results as the table entity * type. * @param continuationToken * The {@link ResultContinuation} to pass with the operation to resume a query, if any. Pass * <code>null</code> for an initial query. * @param options * A {@link TableRequestOptions} object that specifies execution options such as retry policy and timeout * settings for the operation. Specify <code>null</code> to use the request options specified on the * {@link CloudTableClient}. * @param opContext * An {@link OperationContext} object for tracking the current operation. Specify <code>null</code> to * safely ignore operation context. * @return * A {@link ResultSegment} containing a collection of the query results specialized for the * {@link TableEntity} or {@link EntityResolver} type returned by the query. * @throws StorageException * if a Storage service error occurs. */ protected <T extends TableEntity, R> ResultSegment<?> executeQuerySegmentedImpl(final TableQuery<T> queryToExecute, final EntityResolver<R> resolver, final ResultContinuation continuationToken, TableRequestOptions options, OperationContext opContext) throws StorageException { if (opContext == null) { opContext = new OperationContext(); } opContext.initialize(); options = TableRequestOptions.applyDefaults(options, this); Utility.assertContinuationType(continuationToken, ResultContinuationType.TABLE); SegmentedStorageRequest segmentedRequest = new SegmentedStorageRequest(); segmentedRequest.setToken(continuationToken); return ExecutionEngine.executeWithRetry(this, queryToExecute, this.executeQuerySegmentedWithResolverCoreImpl(queryToExecute, resolver, options, segmentedRequest), options.getRetryPolicyFactory(), opContext); } private <T extends TableEntity, R> StorageRequest<CloudTableClient, TableQuery<T>, ResultSegment<T>> executeQuerySegmentedCoreImpl( final TableQuery<T> queryToExecute, final EntityResolver<R> resolver, final TableRequestOptions options, final SegmentedStorageRequest segmentedRequest) { if (resolver == null) { Utility.assertNotNull(SR.QUERY_REQUIRES_VALID_CLASSTYPE_OR_RESOLVER, queryToExecute.getClazzType()); } final StorageRequest<CloudTableClient, TableQuery<T>, ResultSegment<T>> getRequest = new StorageRequest<CloudTableClient, TableQuery<T>, ResultSegment<T>>( options, this.getStorageUri()) { @Override public void setRequestLocationMode() { this.setRequestLocationMode(Utility.getListingLocationMode(segmentedRequest.getToken())); } @Override public HttpURLConnection buildRequest(CloudTableClient client, TableQuery<T> queryRef, OperationContext context) throws Exception { return TableRequest.query(client.getTransformedEndPoint(context).getUri(this.getCurrentLocation()), options, queryToExecute.generateQueryBuilder(), context, queryToExecute.getSourceTableName(), null/* identity */, segmentedRequest.getToken()); } @Override public void signRequest(HttpURLConnection connection, CloudTableClient client, OperationContext context) throws Exception { StorageRequest.signTableRequest(connection, client, -1L, context); } @Override public ResultSegment<T> preProcessResponse(TableQuery<T> queryRef, CloudTableClient client, OperationContext context) throws Exception { if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_OK) { throw TableServiceException.generateTableServiceException(true, this.getResult(), null, this .getConnection().getErrorStream(), options.getTablePayloadFormat()); } return null; } @SuppressWarnings("unchecked") @Override public ResultSegment<T> postProcessResponse(HttpURLConnection connection, TableQuery<T> queryRef, CloudTableClient client, OperationContext context, ResultSegment<T> storageObject) throws Exception { ODataPayload<T> clazzResponse = null; InputStream inStream = connection.getInputStream(); clazzResponse = (ODataPayload<T>) TableDeserializer.parseQueryResponse(inStream, options, queryToExecute.getClazzType(), null, context); final ResultContinuation nextToken = TableResponse.getTableContinuationFromResponse(connection); if (nextToken != null) { nextToken.setTargetLocation(this.getResult().getTargetLocation()); } // Note, setting the token on the SegmentedStorageRequest is // key, this is how the iterator will share the token across executions segmentedRequest.setToken(nextToken); return new ResultSegment<T>(clazzResponse.results, queryToExecute.getTakeCount() == null ? clazzResponse.results.size() : queryToExecute.getTakeCount(), nextToken); } }; return getRequest; } private <T extends TableEntity, R> StorageRequest<CloudTableClient, TableQuery<T>, ResultSegment<R>> executeQuerySegmentedWithResolverCoreImpl( final TableQuery<T> queryToExecute, final EntityResolver<R> resolver, final TableRequestOptions options, final SegmentedStorageRequest segmentedRequest) { if (resolver == null) { Utility.assertNotNull(SR.QUERY_REQUIRES_VALID_CLASSTYPE_OR_RESOLVER, queryToExecute.getClazzType()); } final StorageRequest<CloudTableClient, TableQuery<T>, ResultSegment<R>> getRequest = new StorageRequest<CloudTableClient, TableQuery<T>, ResultSegment<R>>( options, this.getStorageUri()) { @Override public void setRequestLocationMode() { this.setRequestLocationMode(Utility.getListingLocationMode(segmentedRequest.getToken())); } @Override public HttpURLConnection buildRequest(CloudTableClient client, TableQuery<T> queryRef, OperationContext context) throws Exception { return TableRequest.query(client.getTransformedEndPoint(context).getUri(this.getCurrentLocation()), options, queryToExecute.generateQueryBuilder(), context, queryToExecute.getSourceTableName(), null/* identity */, segmentedRequest.getToken()); } @Override public void signRequest(HttpURLConnection connection, CloudTableClient client, OperationContext context) throws Exception { StorageRequest.signTableRequest(connection, client, -1L, context); } @Override public ResultSegment<R> preProcessResponse(TableQuery<T> queryRef, CloudTableClient client, OperationContext context) throws Exception { if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_OK) { throw TableServiceException.generateTableServiceException(true, this.getResult(), null, this .getConnection().getErrorStream(), options.getTablePayloadFormat()); } return null; } @SuppressWarnings("unchecked") @Override public ResultSegment<R> postProcessResponse(HttpURLConnection connection, TableQuery<T> queryRef, CloudTableClient client, OperationContext context, ResultSegment<R> storageObject) throws Exception { ODataPayload<R> resolvedResponse = null; InputStream inStream = connection.getInputStream(); resolvedResponse = (ODataPayload<R>) TableDeserializer.parseQueryResponse(inStream, options, queryToExecute.getClazzType(), resolver, context); final ResultContinuation nextToken = TableResponse.getTableContinuationFromResponse(connection); if (nextToken != null) { nextToken.setTargetLocation(this.getResult().getTargetLocation()); } // Note, setting the token on the SegmentedStorageRequest is // key, this is how the iterator will share the token across executions segmentedRequest.setToken(nextToken); return new ResultSegment<R>(resolvedResponse.results, queryToExecute.getTakeCount() == null ? resolvedResponse.results.size() : queryToExecute.getTakeCount(), nextToken); } }; return getRequest; } protected final StorageUri getTransformedEndPoint(final OperationContext opContext) throws URISyntaxException, StorageException { return this.getCredentials().transformUri(this.getStorageUri(), opContext); } /** * Reserved for internal use. Generates an iterator for a segmented query operation. * * @param queryRef * The {@link TableQuery} to execute. * @param resolver * An {@link EntityResolver} instance which creates a projection of the table query result entities into * the specified type <code>R</code>. Pass <code>null</code> to return the results as the table entity * type. * @param options * A {@link TableRequestOptions} object that specifies execution options such as retry policy and timeout * settings for the operation. Specify <code>null</code> to use the request options specified on the * {@link CloudTableClient}. * @param opContext * An {@link OperationContext} object for tracking the current operation. Specify <code>null</code> to * safely ignore operation context. * @return * An instance of <code>Iterable</code> specialized for the {@link TableEntity} or {@link EntityResolver} * type returned by the query. */ protected <T extends TableEntity, R> Iterable<?> generateIteratorForQuery(final TableQuery<T> queryRef, final EntityResolver<R> resolver, TableRequestOptions options, OperationContext opContext) { if (opContext == null) { opContext = new OperationContext(); } opContext.initialize(); options = TableRequestOptions.applyDefaults(options, this); SegmentedStorageRequest segmentedRequest = new SegmentedStorageRequest(); if (resolver == null) { return new LazySegmentedIterable<CloudTableClient, TableQuery<T>, T>(this.executeQuerySegmentedCoreImpl( queryRef, resolver, options, segmentedRequest), this, queryRef, options.getRetryPolicyFactory(), opContext); } else { return new LazySegmentedIterable<CloudTableClient, TableQuery<T>, R>( this.executeQuerySegmentedWithResolverCoreImpl(queryRef, resolver, options, segmentedRequest), this, queryRef, options.getRetryPolicyFactory(), opContext); } } /** * Queries the service for the {@link ServiceStats}. * * @return {@link ServiceStats} for the given storage service * @throws StorageException * If a storage service error occurred. */ @DoesServiceRequest public ServiceStats getServiceStats() throws StorageException { return this.getServiceStats(null /* options */, null /* opContext */); } /** * Queries the given storage service for the {@link ServiceStats}. * * @param options * A {@link BlobRequestOptions} object that specifies any additional options for the request. Specifying * <code>null</code> will use the default request options from the associated service client ( * {@link CloudBlobClient}). * @param opContext * An {@link OperationContext} object that represents the context for the current operation. This object * is used to track requests to the storage service, and to provide additional runtime information about * the operation. * @return {@link ServiceStats} for the given storage service * @throws StorageException * If a storage service error occurred. */ @DoesServiceRequest public ServiceStats getServiceStats(TableRequestOptions options, OperationContext opContext) throws StorageException { if (opContext == null) { opContext = new OperationContext(); } opContext.initialize(); options = TableRequestOptions.applyDefaults(options, this); return ExecutionEngine.executeWithRetry(this, null, this.getServiceStatsImpl(options, true), options.getRetryPolicyFactory(), opContext); } /** * Retrieves the current {@link ServiceProperties} for the given storage service. This includes Logging, * HourMetrics, MinuteMetrics and CORS configurations. * * @return the {@link ServiceProperties} object representing the current configuration of the service. * @throws StorageException * If a storage service error occurred. */ @DoesServiceRequest public final ServiceProperties downloadServiceProperties() throws StorageException { return this.downloadServiceProperties(null /* options */, null /* opContext */); } /** * Retrieves the current {@link ServiceProperties} for the given storage service. This includes Logging, * HourMetrics, MinuteMetrics and CORS configurations. * * @param options * A {@link BlobRequestOptions} object that specifies any additional options for the request. Specifying * <code>null</code> will use the default request options from the associated service client ( * {@link CloudBlobClient}). * @param opContext * An {@link OperationContext} object that represents the context for the current operation. This object * is used to track requests to the storage service, and to provide additional runtime information about * the operation. * * @return the {@link ServiceProperties} object representing the current configuration of the service. * @throws StorageException * If a storage service error occurred. */ @DoesServiceRequest public final ServiceProperties downloadServiceProperties(TableRequestOptions options, OperationContext opContext) throws StorageException { if (opContext == null) { opContext = new OperationContext(); } opContext.initialize(); options = TableRequestOptions.applyDefaults(options, this); return ExecutionEngine.executeWithRetry(this, null, this.downloadServicePropertiesImpl(options, true), options.getRetryPolicyFactory(), opContext); } /** * Uploads a new {@link ServiceProperties} configuration to the given storage service. This includes Logging, * HourMetrics, MinuteMetrics and CORS configurations. * * @param properties * The {@link ServiceProperties} to upload. * @throws StorageException * If a storage service error occurred. */ @DoesServiceRequest public void uploadServiceProperties(final ServiceProperties properties) throws StorageException { this.uploadServiceProperties(properties, null /* options */, null /* opContext */); } /** * Uploads a new {@link ServiceProperties} configuration to the given storage service. This includes Logging, * HourMetrics, MinuteMetrics and CORS configurations. * * @param properties * The {@link ServiceProperties} to upload. * @param options * A {@link BlobRequestOptions} object that specifies any additional options for the request. Specifying * <code>null</code> will use the default request options from the associated service client ( * {@link CloudBlobClient}). * @param opContext * An {@link OperationContext} object that represents the context for the current operation. This object * is used to track requests to the storage service, and to provide additional runtime information about * the operation. * @throws StorageException * If a storage service error occurred. */ @DoesServiceRequest public void uploadServiceProperties(final ServiceProperties properties, TableRequestOptions options, OperationContext opContext) throws StorageException { if (!Utility.isNullOrEmpty(properties.getDefaultServiceVersion())) { throw new IllegalArgumentException(SR.DEFAULT_SERVICE_VERSION_ONLY_SET_FOR_BLOB_SERVICE); } if (opContext == null) { opContext = new OperationContext(); } opContext.initialize(); options = TableRequestOptions.applyDefaults(options, this); Utility.assertNotNull("properties", properties); ExecutionEngine.executeWithRetry(this, null, this.uploadServicePropertiesImpl(properties, options, opContext, true), options.getRetryPolicyFactory(), opContext); } /** * Gets the {@link TableRequestOptions} that is used for requests associated with this <code>CloudTableClient</code> * * @return * The {@link TableRequestOptions} object containing the values used by this <code>CloudTableClient</code> */ @Override public TableRequestOptions getDefaultRequestOptions() { return this.defaultRequestOptions; } /** * Sets the {@link TableRequestOptions} that is used for any table accessed with this <code>CloudTableClient</code> * object. * * @param defaultRequestOptions * The TableRequestOptions to use. */ public void setDefaultRequestOptions(TableRequestOptions defaultRequestOptions) { Utility.assertNotNull("defaultRequestOptions", defaultRequestOptions); this.defaultRequestOptions = defaultRequestOptions; } /** * Indicates whether path-style URIs are used. * * @return <code>true</code> if path-style URIs are used; otherwise <code>false</code>. */ @Override protected boolean isUsePathStyleUris() { return super.isUsePathStyleUris(); } }