All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.microsoft.windowsazure.services.queue.client.CloudQueue Maven / Gradle / Ivy

There is a newer version: 0.4.6
Show newest version
/**
 * 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.windowsazure.services.queue.client;

import java.io.OutputStream;
import java.io.StringWriter;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.InvalidKeyException;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.HashMap;

import com.microsoft.windowsazure.services.blob.core.storage.SharedAccessSignatureHelper;
import com.microsoft.windowsazure.services.core.storage.DoesServiceRequest;
import com.microsoft.windowsazure.services.core.storage.OperationContext;
import com.microsoft.windowsazure.services.core.storage.StorageCredentialsSharedAccessSignature;
import com.microsoft.windowsazure.services.core.storage.StorageErrorCodeStrings;
import com.microsoft.windowsazure.services.core.storage.StorageException;
import com.microsoft.windowsazure.services.core.storage.StorageExtendedErrorInformation;
import com.microsoft.windowsazure.services.core.storage.utils.PathUtility;
import com.microsoft.windowsazure.services.core.storage.utils.UriQueryBuilder;
import com.microsoft.windowsazure.services.core.storage.utils.Utility;
import com.microsoft.windowsazure.services.core.storage.utils.implementation.BaseResponse;
import com.microsoft.windowsazure.services.core.storage.utils.implementation.ExecutionEngine;
import com.microsoft.windowsazure.services.core.storage.utils.implementation.StorageOperation;

/**
 * This class represents a queue in the Windows Azure Queue service.
 */
public final class CloudQueue {

    /**
     * Gets the first message from a list of queue messages, if any.
     * 
     * @param messages
     *            The Iterable collection of {@link CloudQueueMessage} objects to get the first message
     *            from.
     * 
     * @return The first {@link CloudQueueMessage} from the list of queue messages, or null if the list is
     *         empty.
     */
    static CloudQueueMessage getFirstOrNull(final Iterable messages) {
        for (final CloudQueueMessage m : messages) {
            return m;
        }

        return null;
    }

    /**
     * The name of the queue.
     */
    private String name;

    /**
     * The absolute URI of the queue.
     */
    URI uri;

    /**
     * A reference to the queue's associated service client.
     */
    CloudQueueClient queueServiceClient;

    /**
     * The queue's Metadata collection.
     */
    HashMap metadata;

    /**
     * The queue's approximate message count, as reported by the server.
     */
    long approximateMessageCount;

    /**
     * The CloudQueue class using the specified address and client.
     * 
     * @param queueAddress
     *            A String that represents either the absolute URI to the queue, or the queue name.
     * @param client
     *            A {@link CloudQueueClient} object that represents the associated service client, and that specifies
     *            the endpoint for the Queue service.
     * 
     * @throws URISyntaxException
     *             If the resource URI is invalid.
     * @throws StorageException
     */
    public CloudQueue(final String queueAddress, final CloudQueueClient client) throws URISyntaxException,
            StorageException {
        this(PathUtility.appendPathToUri(client.getEndpoint(), queueAddress), client);
    }

    /**
     * Creates an instance of the CloudQueue class using the specified queue URI and client.
     * 
     * @param uri
     *            A java.net.URI object that represents the absolute URI of the queue.
     * @param client
     *            A {@link CloudQueueClient} object that represents the associated service client, and that specifies
     *            the endpoint for the Queue service.
     * @throws StorageException
     * @throws URISyntaxException
     */
    public CloudQueue(final URI uri, final CloudQueueClient client) throws URISyntaxException, StorageException {
        this.uri = uri;
        this.name = PathUtility.getQueueNameFromUri(uri, client.isUsePathStyleUris());
        this.queueServiceClient = client;
        this.shouldEncodeMessage = true;
        this.parseQueryAndVerify(this.uri, client, client.isUsePathStyleUris());
    }

    /**
     * Adds a message to the back of the queue with the default options.
     * 
     * @param message
     *            A {@link CloudQueueMessage} object that specifies the message to add.
     * 
     * @throws StorageException
     *             If a storage service error occurred during the operation.
     */
    @DoesServiceRequest
    public void addMessage(final CloudQueueMessage message) throws StorageException {
        this.addMessage(message, 0, 0, null, null);
    }

    /**
     * Adds a message to the back of the queue with the specified options.
     * 
     * @param message
     *            A {@link CloudQueueMessage} object that specifies the message to add.
     * 
     * @param timeToLiveInSeconds
     *            The maximum time to allow the message to be in the queue. A value of zero will set the time-to-live to
     *            the service default value of seven days.
     * 
     * @param initialVisibilityDelayInSeconds
     *            The length of time during which the message will be invisible, starting when it is added to the queue,
     *            or 0 to make the message visible immediately. This value must be greater than or equal to zero and
     *            less than or equal to the time-to-live value.
     * 
     * @param options
     *            A {@link QueueRequestOptions} object that specifies any additional options for the request. Specifying
     *            null will use the default request options from the associated service client (
     *            {@link CloudQueueClient}).
     * 
     * @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 during the operation.
     */
    @DoesServiceRequest
    public void addMessage(final CloudQueueMessage message, final int timeToLiveInSeconds,
            final int initialVisibilityDelayInSeconds, QueueRequestOptions options, OperationContext opContext)
            throws StorageException {
        Utility.assertNotNull("message", message);
        Utility.assertNotNull("messageContent", message.getMessageContentAsByte());
        Utility.assertInBounds("timeToLiveInSeconds", timeToLiveInSeconds, 0,
                QueueConstants.MAX_TIME_TO_LIVE_IN_SECONDS);

        final int realTimeToLiveInSeconds = timeToLiveInSeconds == 0 ? QueueConstants.MAX_TIME_TO_LIVE_IN_SECONDS
                : timeToLiveInSeconds;
        Utility.assertInBounds("initialVisibilityDelayInSeconds", initialVisibilityDelayInSeconds, 0,
                realTimeToLiveInSeconds - 1);

        final String stringToSend = message.getMessageContentForTransfer(this.shouldEncodeMessage);

        if (opContext == null) {
            opContext = new OperationContext();
        }

        if (options == null) {
            options = new QueueRequestOptions();
        }

        opContext.initialize();
        options.applyDefaults(this.queueServiceClient);

        final StorageOperation impl = new StorageOperation(
                options) {

            @Override
            public Void execute(final CloudQueueClient client, final CloudQueue queue, final OperationContext opContext)
                    throws Exception {

                final HttpURLConnection request = QueueRequest.putMessage(queue.getMessageRequestAddress(opContext),
                        this.getRequestOptions().getTimeoutIntervalInMs(), initialVisibilityDelayInSeconds,
                        timeToLiveInSeconds, opContext);

                final byte[] messageBytes = QueueRequest.generateMessageRequestBody(stringToSend);

                client.getCredentials().signRequest(request, messageBytes.length);
                final OutputStream outStreamRef = request.getOutputStream();
                outStreamRef.write(messageBytes);

                ExecutionEngine.processRequest(request, opContext, this.getResult());

                if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_CREATED) {
                    this.setNonExceptionedRetryableFailure(true);
                    return null;
                }

                return null;
            }
        };

        ExecutionEngine.executeWithRetry(this.queueServiceClient, this, impl, options.getRetryPolicyFactory(),
                opContext);
    }

    /**
     * Clears all messages from the queue, using the default request options.
     * 
     * @throws StorageException
     *             If a storage service error occurred during the operation.
     */
    @DoesServiceRequest
    public void clear() throws StorageException {
        this.clear(null, null);
    }

    /**
     * Clears all messages from the queue, using the specified request options and operation context.
     * 
     * @param options
     *            A {@link QueueRequestOptions} object that specifies any additional options for the request. Specifying
     *            null will use the default request options from the associated service client (
     *            {@link CloudQueueClient}).
     * @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 during the operation.
     */
    @DoesServiceRequest
    public void clear(QueueRequestOptions options, OperationContext opContext) throws StorageException {
        if (opContext == null) {
            opContext = new OperationContext();
        }

        if (options == null) {
            options = new QueueRequestOptions();
        }

        opContext.initialize();
        options.applyDefaults(this.queueServiceClient);

        final StorageOperation impl = new StorageOperation(
                options) {

            @Override
            public Void execute(final CloudQueueClient client, final CloudQueue queue, final OperationContext opContext)
                    throws Exception {

                final HttpURLConnection request = QueueRequest.clearMessages(queue.getMessageRequestAddress(opContext),
                        this.getRequestOptions().getTimeoutIntervalInMs(), opContext);

                client.getCredentials().signRequest(request, -1L);

                ExecutionEngine.processRequest(request, opContext, this.getResult());

                if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_NO_CONTENT) {
                    this.setNonExceptionedRetryableFailure(true);
                }

                return null;
            }
        };

        ExecutionEngine.executeWithRetry(this.queueServiceClient, this, impl, options.getRetryPolicyFactory(),
                opContext);
    }

    /**
     * Creates the queue in the storage service with default request options.
     * 
     * @throws StorageException
     *             If a storage service error occurred during the operation.
     */
    @DoesServiceRequest
    public void create() throws StorageException {
        this.create(null, null);
    }

    /**
     * Creates the queue in the storage service using the specified request options and operation context.
     * 
     * @param options
     *            A {@link QueueRequestOptions} object that specifies any additional options for the request. Specifying
     *            null will use the default request options from the associated service client (
     *            {@link CloudQueueClient}).
     * @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 during the operation.
     */
    @DoesServiceRequest
    public void create(QueueRequestOptions options, OperationContext opContext) throws StorageException {
        if (opContext == null) {
            opContext = new OperationContext();
        }

        if (options == null) {
            options = new QueueRequestOptions();
        }

        opContext.initialize();
        options.applyDefaults(this.queueServiceClient);

        final StorageOperation impl = new StorageOperation(
                options) {

            @Override
            public Void execute(final CloudQueueClient client, final CloudQueue queue, final OperationContext opContext)
                    throws Exception {
                final HttpURLConnection request = QueueRequest.create(queue.getTransformedAddress(opContext), this
                        .getRequestOptions().getTimeoutIntervalInMs(), opContext);

                QueueRequest.addMetadata(request, queue.metadata, opContext);
                client.getCredentials().signRequest(request, 0L);

                ExecutionEngine.processRequest(request, opContext, this.getResult());

                if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_CREATED
                        && this.getResult().getStatusCode() != HttpURLConnection.HTTP_NO_CONTENT) {
                    this.setNonExceptionedRetryableFailure(true);
                }

                return null;
            }
        };

        ExecutionEngine.executeWithRetry(this.queueServiceClient, this, impl, options.getRetryPolicyFactory(),
                opContext);
    }

    /**
     * Creates the queue in the storage service using default request options if it does not already exist.
     * 
     * @return A value of true if the queue is created in the storage service, otherwise false
     *         .
     * 
     * @throws StorageException
     *             If a storage service error occurred during the operation.
     */
    @DoesServiceRequest
    public boolean createIfNotExist() throws StorageException {
        return this.createIfNotExist(null, null);
    }

    /**
     * Creates the queue in the storage service with the specified request options and operation context if it does not
     * already exist.
     * 
     * @param options
     *            A {@link QueueRequestOptions} object that specifies any additional options for the request. Specifying
     *            null will use the default request options from the associated service client (
     *            {@link CloudQueueClient}).
     * @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 value of true if the queue is created in the storage service, otherwise false
     *         .
     * 
     * @throws StorageException
     *             If a storage service error occurred during the operation.
     */
    @DoesServiceRequest
    public boolean createIfNotExist(QueueRequestOptions options, OperationContext opContext) throws StorageException {
        if (opContext == null) {
            opContext = new OperationContext();
        }

        if (options == null) {
            options = new QueueRequestOptions();
        }

        opContext.initialize();
        options.applyDefaults(this.queueServiceClient);

        final StorageOperation impl = new StorageOperation(
                options) {

            @Override
            public Boolean execute(final CloudQueueClient client, final CloudQueue queue,
                    final OperationContext opContext) throws Exception {
                final HttpURLConnection request = QueueRequest.create(queue.getTransformedAddress(opContext), this
                        .getRequestOptions().getTimeoutIntervalInMs(), opContext);

                QueueRequest.addMetadata(request, queue.metadata, opContext);
                client.getCredentials().signRequest(request, 0L);

                ExecutionEngine.processRequest(request, opContext, this.getResult());

                if (this.getResult().getStatusCode() == HttpURLConnection.HTTP_CREATED) {
                    return true;
                }
                else if (this.getResult().getStatusCode() == HttpURLConnection.HTTP_NO_CONTENT) {
                    return false;
                }
                else if (this.getResult().getStatusCode() == HttpURLConnection.HTTP_CONFLICT) {
                    final StorageException potentialConflictException = StorageException.translateException(request,
                            null, opContext);

                    StorageExtendedErrorInformation extendedInfo = potentialConflictException
                            .getExtendedErrorInformation();
                    if (extendedInfo == null) {
                        // If we cant validate the error then the error must be surfaced to the user.
                        throw potentialConflictException;
                    }

                    if (!extendedInfo.getErrorCode().equals(StorageErrorCodeStrings.QUEUE_ALREADY_EXISTS)) {
                        this.setException(potentialConflictException);
                        this.setNonExceptionedRetryableFailure(true);
                    }
                }
                else {
                    this.setNonExceptionedRetryableFailure(true);
                }

                return false;
            }
        };

        return ExecutionEngine.executeWithRetry(this.queueServiceClient, this, impl, options.getRetryPolicyFactory(),
                opContext);
    }

    /**
     * Deletes the queue from the storage service.
     * 
     * @throws StorageException
     *             If a storage service error occurred during the operation.
     */
    @DoesServiceRequest
    public void delete() throws StorageException {
        this.delete(null, null);
    }

    /**
     * Deletes the queue from the storage service, using the specified request options and operation context.
     * 
     * @param options
     *            A {@link QueueRequestOptions} object that specifies any additional options for the request. Specifying
     *            null will use the default request options from the associated service client (
     *            {@link CloudQueueClient}).
     * @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 during the operation.
     */
    @DoesServiceRequest
    public void delete(QueueRequestOptions options, OperationContext opContext) throws StorageException {
        if (opContext == null) {
            opContext = new OperationContext();
        }

        if (options == null) {
            options = new QueueRequestOptions();
        }

        opContext.initialize();
        options.applyDefaults(this.queueServiceClient);

        final StorageOperation impl = new StorageOperation(
                options) {
            @Override
            public Void execute(final CloudQueueClient client, final CloudQueue queue, final OperationContext opContext)
                    throws Exception {
                final HttpURLConnection request = QueueRequest.delete(queue.getTransformedAddress(opContext), this
                        .getRequestOptions().getTimeoutIntervalInMs(), opContext);

                client.getCredentials().signRequest(request, -1L);

                ExecutionEngine.processRequest(request, opContext, this.getResult());

                if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_NO_CONTENT) {
                    this.setNonExceptionedRetryableFailure(true);
                }

                return null;
            }
        };

        ExecutionEngine.executeWithRetry(this.queueServiceClient, this, impl, options.getRetryPolicyFactory(),
                opContext);

    }

    /**
     * Deletes the queue from the storage service if it exists.
     * 
     * @return A value of true if the queue existed in the storage service and has been deleted, otherwise
     *         false.
     * 
     * @throws StorageException
     *             If a storage service error occurred during the operation.
     */
    @DoesServiceRequest
    public boolean deleteIfExists() throws StorageException {
        return this.deleteIfExists(null, null);
    }

    /**
     * Deletes the queue from the storage service using the specified request options and operation context, if it
     * exists.
     * 
     * @param options
     *            A {@link QueueRequestOptions} object that specifies any additional options for the request. Specifying
     *            null will use the default request options from the associated service client (
     *            {@link CloudQueueClient}).
     * @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 value of true if the queue existed in the storage service and has been deleted, otherwise
     *         false.
     * 
     * @throws StorageException
     *             If a storage service error occurred during the operation.
     */
    @DoesServiceRequest
    public boolean deleteIfExists(QueueRequestOptions options, OperationContext opContext) throws StorageException {
        if (opContext == null) {
            opContext = new OperationContext();
        }

        if (options == null) {
            options = new QueueRequestOptions();
        }

        opContext.initialize();
        options.applyDefaults(this.queueServiceClient);

        final StorageOperation impl = new StorageOperation(
                options) {
            @Override
            public Boolean execute(final CloudQueueClient client, final CloudQueue queue,
                    final OperationContext opContext) throws Exception {
                final HttpURLConnection request = QueueRequest.delete(queue.getTransformedAddress(opContext), this
                        .getRequestOptions().getTimeoutIntervalInMs(), opContext);

                client.getCredentials().signRequest(request, -1L);

                ExecutionEngine.processRequest(request, opContext, this.getResult());

                if (this.getResult().getStatusCode() == HttpURLConnection.HTTP_NO_CONTENT) {
                    return true;
                }
                else if (this.getResult().getStatusCode() == HttpURLConnection.HTTP_NOT_FOUND) {
                    return false;
                }
                else {
                    this.setNonExceptionedRetryableFailure(true);
                    return false;
                }
            }
        };

        return ExecutionEngine.executeWithRetry(this.queueServiceClient, this, impl, options.getRetryPolicyFactory(),
                opContext);

    }

    /**
     * Deletes the specified message from the queue.
     * 
     * @param message
     *            A {@link CloudQueueMessage} object that specifies the message to delete.
     * 
     * @throws StorageException
     *             If a storage service error occurred during the operation.
     */
    @DoesServiceRequest
    public void deleteMessage(final CloudQueueMessage message) throws StorageException {
        this.deleteMessage(message, null, null);
    }

    /**
     * Deletes the specified message from the queue, using the specified request options and operation context.
     * 
     * @param message
     *            A {@link CloudQueueMessage} object that specifies the message to delete.
     * 
     * @param options
     *            A {@link QueueRequestOptions} object that specifies any additional options for the request. Specifying
     *            null will use the default request options from the associated service client (
     *            {@link CloudQueueClient}).
     * 
     * @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 during the operation.
     */
    @DoesServiceRequest
    public void deleteMessage(final CloudQueueMessage message, QueueRequestOptions options, OperationContext opContext)
            throws StorageException {
        Utility.assertNotNull("message", message);
        Utility.assertNotNullOrEmpty("messageId", message.id);
        Utility.assertNotNullOrEmpty("popReceipt", message.popReceipt);

        if (opContext == null) {
            opContext = new OperationContext();
        }

        if (options == null) {
            options = new QueueRequestOptions();
        }

        opContext.initialize();
        options.applyDefaults(this.queueServiceClient);

        final String messageId = message.getId();
        final String messagePopReceipt = message.getPopReceipt();

        final StorageOperation impl = new StorageOperation(
                options) {

            @Override
            public Void execute(final CloudQueueClient client, final CloudQueue queue, final OperationContext opContext)
                    throws Exception {

                final HttpURLConnection request = QueueRequest.deleteMessage(queue.getIndividualMessageAddress(
                        messageId, opContext), this.getRequestOptions().getTimeoutIntervalInMs(), messagePopReceipt,
                        opContext);

                client.getCredentials().signRequest(request, -1L);

                ExecutionEngine.processRequest(request, opContext, this.getResult());

                if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_NO_CONTENT) {
                    this.setNonExceptionedRetryableFailure(true);
                }

                return null;
            }
        };

        ExecutionEngine.executeWithRetry(this.queueServiceClient, this, impl, options.getRetryPolicyFactory(),
                opContext);
    }

    /**
     * Downloads the queue's metadata and approximate message count value.
     * 
     * @throws StorageException
     *             If a storage service error occurred during the operation.
     */
    @DoesServiceRequest
    public void downloadAttributes() throws StorageException {
        this.downloadAttributes(null, null);
    }

    /**
     * Downloads the queue's metadata and approximate message count value, using the specified request options and
     * operation context.
     * 
     * @param options
     *            A {@link QueueRequestOptions} object that specifies any additional options for the request. Specifying
     *            null will use the default request options from the associated service client (
     *            {@link CloudQueue}).
     * @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 during the operation.
     */
    @DoesServiceRequest
    public void downloadAttributes(QueueRequestOptions options, OperationContext opContext) throws StorageException {
        if (opContext == null) {
            opContext = new OperationContext();
        }

        if (options == null) {
            options = new QueueRequestOptions();
        }

        opContext.initialize();
        options.applyDefaults(this.queueServiceClient);

        final StorageOperation impl = new StorageOperation(
                options) {

            @Override
            public Void execute(final CloudQueueClient client, final CloudQueue queue, final OperationContext opContext)
                    throws Exception {
                final HttpURLConnection request = QueueRequest.downloadAttributes(
                        queue.getTransformedAddress(opContext), this.getRequestOptions().getTimeoutIntervalInMs(),
                        opContext);

                client.getCredentials().signRequest(request, -1L);

                ExecutionEngine.processRequest(request, opContext, this.getResult());

                if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_OK) {
                    this.setNonExceptionedRetryableFailure(true);
                    return null;
                }

                queue.metadata = BaseResponse.getMetadata(request);
                queue.approximateMessageCount = QueueResponse.getApproximateMessageCount(request);
                return null;
            }
        };

        ExecutionEngine.executeWithRetry(this.queueServiceClient, this, impl, options.getRetryPolicyFactory(),
                opContext);
    }

    /**
     * Returns a value that indicates whether the queue exists in the storage service.
     * 
     * @return true if the queue exists in the storage service, otherwise false.
     * 
     * @throws StorageException
     *             If a storage service error occurred during the operation.
     */
    @DoesServiceRequest
    public boolean exists() throws StorageException {
        return this.exists(null, null);
    }

    /**
     * Returns a value that indicates whether the queue exists in the storage service, using the specified request
     * options and operation context.
     * 
     * @param options
     *            A {@link QueueRequestOptions} object that specifies any additional options for the request. Specifying
     *            null will use the default request options from the associated service client (
     *            {@link CloudQueueClient}).
     * @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 true if the queue exists in the storage service, otherwise false.
     * 
     * @throws StorageException
     *             If a storage service error occurred during the operation.
     */
    @DoesServiceRequest
    public boolean exists(QueueRequestOptions options, OperationContext opContext) throws StorageException {
        if (opContext == null) {
            opContext = new OperationContext();
        }

        if (options == null) {
            options = new QueueRequestOptions();
        }

        opContext.initialize();
        options.applyDefaults(this.queueServiceClient);

        final StorageOperation impl = new StorageOperation(
                options) {

            @Override
            public Boolean execute(final CloudQueueClient client, final CloudQueue queue,
                    final OperationContext opContext) throws Exception {
                final HttpURLConnection request = QueueRequest.downloadAttributes(
                        queue.getTransformedAddress(opContext), this.getRequestOptions().getTimeoutIntervalInMs(),
                        opContext);

                client.getCredentials().signRequest(request, -1L);

                ExecutionEngine.processRequest(request, opContext, this.getResult());

                if (this.getResult().getStatusCode() == HttpURLConnection.HTTP_OK) {
                    return Boolean.valueOf(true);
                }
                else if (this.getResult().getStatusCode() == HttpURLConnection.HTTP_NOT_FOUND) {
                    return Boolean.valueOf(false);
                }
                else {
                    this.setNonExceptionedRetryableFailure(true);
                    // return false instead of null to avoid SCA issues
                    return false;
                }
            }
        };

        return ExecutionEngine.executeWithRetry(this.queueServiceClient, this, impl, options.getRetryPolicyFactory(),
                opContext);
    }

    /**
     * Gets the approximate messages count of the queue. This value is initialized by a request to
     * {@link #downloadAttributes} and represents the approximate message count when that request completed.
     * 
     * @return A Long object that represents the approximate messages count of the queue.
     */
    public long getApproximateMessageCount() {
        return this.approximateMessageCount;
    }

    /**
     * Get a single message request (Used internally only).
     * 
     * @return The URI for a single message request.
     * 
     * @throws URISyntaxException
     *             If the resource URI is invalid.
     * @throws StorageException
     */
    URI getIndividualMessageAddress(final String messageId, final OperationContext opContext)
            throws URISyntaxException, StorageException {
        return PathUtility.appendPathToUri(this.getMessageRequestAddress(opContext), messageId);
    }

    /**
     * Get the message request base address (Used internally only).
     * 
     * @return The message request URI.
     * 
     * @throws URISyntaxException
     *             If the resource URI is invalid.
     * @throws StorageException
     */
    URI getMessageRequestAddress(final OperationContext opContext) throws URISyntaxException, StorageException {
        if (this.messageRequestAddress == null) {
            this.messageRequestAddress = PathUtility.appendPathToUri(this.getTransformedAddress(opContext),
                    QueueConstants.MESSAGES);
        }

        return this.messageRequestAddress;
    }

    /**
     * Gets the metadata collection for the queue as stored in this CloudQueue object. This value is
     * initialized with the metadata from the queue by a call to {@link #downloadAttributes}, and is set on the queue
     * with a call to {@link #uploadMetadata}.
     * 
     * @return A java.util.HashMap object that represents the metadata for the queue.
     */
    public HashMap getMetadata() {
        return this.metadata;
    }

    /**
     * Gets the name of the queue.
     * 
     * @return A String object that represents the name of the queue.
     */
    public String getName() {
        return this.name;
    }

    /**
     * Gets the queue service client associated with this queue.
     * 
     * @return A {@link CloudQueueClient} object that represents the service client associated with this queue.
     */
    public CloudQueueClient getServiceClient() {
        return this.queueServiceClient;
    }

    /**
     * Gets the value indicating whether the message should be base-64 encoded.
     * 
     * @return A Boolean that represents whether the message should be base-64 encoded.
     */
    public boolean getShouldEncodeMessage() {
        return this.shouldEncodeMessage;
    }

    /**
     * Gets the absolute URI for this queue.
     * 
     * @return A java.net.URI object that represents the URI for this queue.
     */
    public URI getUri() {
        return this.uri;
    }

    /**
     * Peeks a message from the queue. A peek request retrieves a message from the front of the queue without changing
     * its visibility.
     * 
     * @return An {@link CloudQueueMessage} object that represents a message in this queue.
     * 
     * @throws StorageException
     *             If a storage service error occurred during the operation.
     */
    @DoesServiceRequest
    public CloudQueueMessage peekMessage() throws StorageException {
        return this.peekMessage(null, null);
    }

    /**
     * Peeks a message from the queue, using the specified request options and operation context. A peek request
     * retrieves a message from the front of the queue without changing its visibility.
     * 
     * @param options
     *            A {@link QueueRequestOptions} object that specifies any additional options for the request. Specifying
     *            null will use the default request options from the associated service client (
     *            {@link CloudQueueClient}).
     * @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 An {@link CloudQueueMessage} object that represents the requested message from the queue.
     * 
     * @throws StorageException
     *             If a storage service error occurred during the operation.
     */
    @DoesServiceRequest
    public CloudQueueMessage peekMessage(final QueueRequestOptions options, final OperationContext opContext)
            throws StorageException {
        return getFirstOrNull(this.peekMessages(1, null, null));
    }

    /**
     * Peeks a specified number of messages from the queue. A peek request retrieves messages from the front of the
     * queue without changing their visibility.
     * 
     * @param numberOfMessages
     *            The number of messages to retrieve.
     * 
     * @return An enumerable collection of {@link CloudQueueMessage} objects that represents the requested messages from
     *         the queue.
     * 
     * @throws StorageException
     *             If a storage service error occurred during the operation.
     */
    @DoesServiceRequest
    public Iterable peekMessages(final int numberOfMessages) throws StorageException {
        return this.peekMessages(numberOfMessages, null, null);
    }

    /**
     * Peeks a set of messages from the queue, using the specified request options and operation context. A peek request
     * retrieves messages from the front of the queue without changing their visibility.
     * 
     * @param numberOfMessages
     *            The number of messages to retrieve.
     * 
     * @param options
     *            A {@link QueueRequestOptions} object that specifies any additional options for the request. Specifying
     *            null will use the default request options from the associated service client (
     *            {@link CloudQueueClient}).
     * @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 An enumerable collection of {@link CloudQueueMessage} objects that represents the requested messages from
     *         the queue.
     * 
     * @throws StorageException
     *             If a storage service error occurred during the operation.
     */
    @DoesServiceRequest
    public Iterable peekMessages(final int numberOfMessages, QueueRequestOptions options,
            OperationContext opContext) throws StorageException {
        Utility.assertInBounds("numberOfMessages", numberOfMessages, 1, QueueConstants.MAX_NUMBER_OF_MESSAGES_TO_PEEK);

        if (opContext == null) {
            opContext = new OperationContext();
        }

        if (options == null) {
            options = new QueueRequestOptions();
        }

        opContext.initialize();
        options.applyDefaults(this.queueServiceClient);

        final StorageOperation> impl = new StorageOperation>(
                options) {

            @Override
            public ArrayList execute(final CloudQueueClient client, final CloudQueue queue,
                    final OperationContext opContext) throws Exception {

                final HttpURLConnection request = QueueRequest.peekMessages(queue.getMessageRequestAddress(opContext),
                        this.getRequestOptions().getTimeoutIntervalInMs(), numberOfMessages, opContext);

                client.getCredentials().signRequest(request, -1L);

                ExecutionEngine.processRequest(request, opContext, this.getResult());

                if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_OK) {
                    this.setNonExceptionedRetryableFailure(true);
                    return null;
                }
                else {
                    return QueueDeserializationHelper.readMessages(request.getInputStream(), queue.shouldEncodeMessage);
                }
            }
        };

        return ExecutionEngine.executeWithRetry(this.queueServiceClient, this, impl, options.getRetryPolicyFactory(),
                opContext);
    }

    /**
     * Retrieves a message from the front of the queue using the default request options. This operation marks the
     * retrieved message as invisible in the queue for the default visibility timeout period.
     * 
     * @return An {@link CloudQueueMessage} object that represents a message in this queue.
     * 
     * @throws StorageException
     *             If a storage service error occurred during the operation.
     */
    @DoesServiceRequest
    public CloudQueueMessage retrieveMessage() throws StorageException {
        return this.retrieveMessage(QueueConstants.DEFAULT_VISIBILITY_MESSAGE_TIMEOUT_IN_SECONDS, null, null);
    }

    /**
     * Retrieves a message from the front of the queue, using the specified request options and operation context. This
     * operation marks the retrieved message as invisible in the queue for the specified visibility timeout period.
     * 
     * @param visibilityTimeoutInSeconds
     *            Specifies the visibility timeout for the message, in seconds.
     * 
     * @param options
     *            A {@link QueueRequestOptions} object that specifies any additional options for the request. Specifying
     *            null will use the default request options from the associated service client (
     *            {@link CloudQueueClient}).
     * @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 An {@link CloudQueueMessage} object that represents a message in this queue.
     * 
     * @throws StorageException
     *             If a storage service error occurred during the operation.
     */
    @DoesServiceRequest
    public CloudQueueMessage retrieveMessage(final int visibilityTimeoutInSeconds, final QueueRequestOptions options,
            final OperationContext opContext) throws StorageException {
        return getFirstOrNull(this.retrieveMessages(1, visibilityTimeoutInSeconds, options, opContext));
    }

    /**
     * Retrieves the specified number of messages from the front of the queue using the default request options. This
     * operation marks the retrieved messages as invisible in the queue for the default visibility timeout period.
     * 
     * @param numberOfMessages
     *            The number of messages to retrieve.
     * 
     * @return An enumerable collection of {@link CloudQueueMessage} objects that represents the retrieved messages from
     *         the queue.
     * 
     * @throws StorageException
     *             If a storage service error occurred during the operation.
     */
    @DoesServiceRequest
    public Iterable retrieveMessages(final int numberOfMessages) throws StorageException {
        return this.retrieveMessages(numberOfMessages, QueueConstants.DEFAULT_VISIBILITY_MESSAGE_TIMEOUT_IN_SECONDS,
                null, null);
    }

    /**
     * Retrieves the specified number of messages from the front of the queue using the specified request options and
     * operation context. This operation marks the retrieved messages as invisible in the queue for the default
     * visibility timeout period.
     * 
     * @param numberOfMessages
     *            The number of messages to retrieve.
     * 
     * @param visibilityTimeoutInSeconds
     *            Specifies the visibility timeout for the retrieved messages, in seconds.
     * 
     * @param options
     *            A {@link QueueRequestOptions} object that specifies any additional options for the request. Specifying
     *            null will use the default request options from the associated service client (
     *            {@link CloudQueueClient}).
     * @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 An enumerable collection of {@link CloudQueueMessage} objects that represents the messages retrieved from
     *         the queue.
     * 
     * @throws StorageException
     *             If a storage service error occurred during the operation.
     */
    @DoesServiceRequest
    public Iterable retrieveMessages(final int numberOfMessages,
            final int visibilityTimeoutInSeconds, QueueRequestOptions options, OperationContext opContext)
            throws StorageException {
        Utility.assertInBounds("numberOfMessages", numberOfMessages, 1, QueueConstants.MAX_NUMBER_OF_MESSAGES_TO_PEEK);
        Utility.assertInBounds("visibilityTimeoutInSeconds", visibilityTimeoutInSeconds, 0,
                QueueConstants.MAX_TIME_TO_LIVE_IN_SECONDS);

        if (opContext == null) {
            opContext = new OperationContext();
        }

        if (options == null) {
            options = new QueueRequestOptions();
        }

        opContext.initialize();
        options.applyDefaults(this.queueServiceClient);

        final StorageOperation> impl = new StorageOperation>(
                options) {

            @Override
            public ArrayList execute(final CloudQueueClient client, final CloudQueue queue,
                    final OperationContext opContext) throws Exception {

                final HttpURLConnection request = QueueRequest.retrieveMessages(
                        queue.getMessageRequestAddress(opContext), this.getRequestOptions().getTimeoutIntervalInMs(),
                        numberOfMessages, visibilityTimeoutInSeconds, opContext);

                client.getCredentials().signRequest(request, -1L);

                ExecutionEngine.processRequest(request, opContext, this.getResult());

                if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_OK) {
                    this.setNonExceptionedRetryableFailure(true);
                    return null;
                }
                else {
                    return QueueDeserializationHelper.readMessages(request.getInputStream(), queue.shouldEncodeMessage);
                }
            }
        };

        return ExecutionEngine.executeWithRetry(this.queueServiceClient, this, impl, options.getRetryPolicyFactory(),
                opContext);
    }

    /**
     * Sets the metadata collection of name-value pairs to be set on the queue with an {@link #uploadMetadata} call.
     * This collection will overwrite any existing queue metadata. If this is set to an empty collection, the queue
     * metadata will be cleared on an {@link #uploadMetadata} call.
     * 
     * @param metadata
     *            A java.util.HashMap object that represents the metadata being assigned to the queue.
     */
    public void setMetadata(final HashMap metadata) {
        this.metadata = metadata;
    }

    /**
     * Sets the flag indicating whether the message should be base-64 encoded.
     * 
     * @param shouldEncodeMessage
     *            The value indicates whether the message should be base-64 encoded.
     */
    public void setShouldEncodeMessage(final boolean shouldEncodeMessage) {
        this.shouldEncodeMessage = shouldEncodeMessage;
    }

    /**
     * Sets the name of the queue.
     * 
     * @param name
     *            A String that represents the name being assigned to the queue.
     */
    void setName(final String name) {
        this.name = name;
    }

    /**
     * Updates the specified message in the queue with a new visibility timeout value in seconds.
     * 
     * @param message
     *            The {@link CloudQueueMessage} to update in the queue.
     * 
     * @param visibilityTimeoutInSeconds
     *            Specifies the new visibility timeout for the message, in seconds.
     * 
     * @throws StorageException
     *             If a storage service error occurred during the operation.
     */
    public void updateMessage(final CloudQueueMessage message, final int visibilityTimeoutInSeconds)
            throws StorageException {
        this.updateMessage(message, visibilityTimeoutInSeconds, EnumSet.of(MessageUpdateFields.VISIBILITY), null, null);
    }

    /**
     * Updates a message in the queue, using the specified request options and operation context.
     * 
     * @param message
     *            The {@link CloudQueueMessage} to update in the queue.
     * 
     * @param visibilityTimeoutInSeconds
     *            Specifies the new visibility timeout for the message, in seconds.
     * 
     * @param messageUpdateFields
     *            An EnumSet of {@link MessageUpdateFields} values that specifies which parts of the
     *            message are to be updated.
     * 
     * @param options
     *            A {@link QueueRequestOptions} object that specifies any additional options for the request. Specifying
     *            null will use the default request options from the associated service client (
     *            {@link CloudQueueClient}).
     * 
     * @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 during the operation.
     */
    @DoesServiceRequest
    public void updateMessage(final CloudQueueMessage message, final int visibilityTimeoutInSeconds,
            final EnumSet messageUpdateFields, QueueRequestOptions options,
            OperationContext opContext) throws StorageException {
        Utility.assertNotNull("message", message);
        Utility.assertNotNullOrEmpty("messageId", message.id);
        Utility.assertNotNullOrEmpty("popReceipt", message.popReceipt);

        Utility.assertInBounds("visibilityTimeoutInSeconds", visibilityTimeoutInSeconds, 0,
                QueueConstants.MAX_TIME_TO_LIVE_IN_SECONDS);

        final String stringToSend = message.getMessageContentForTransfer(this.shouldEncodeMessage);

        if (opContext == null) {
            opContext = new OperationContext();
        }

        if (options == null) {
            options = new QueueRequestOptions();
        }

        opContext.initialize();
        options.applyDefaults(this.queueServiceClient);

        final StorageOperation impl = new StorageOperation(
                options) {

            @Override
            public Void execute(final CloudQueueClient client, final CloudQueue queue, final OperationContext opContext)
                    throws Exception {

                final HttpURLConnection request = QueueRequest.updateMessage(queue.getIndividualMessageAddress(
                        message.getId(), opContext), this.getRequestOptions().getTimeoutIntervalInMs(), message
                        .getPopReceipt(), visibilityTimeoutInSeconds, opContext);

                if (messageUpdateFields.contains(MessageUpdateFields.CONTENT)) {
                    final byte[] messageBytes = QueueRequest.generateMessageRequestBody(stringToSend);

                    client.getCredentials().signRequest(request, messageBytes.length);
                    final OutputStream outStreamRef = request.getOutputStream();
                    outStreamRef.write(messageBytes);
                }
                else {
                    request.setFixedLengthStreamingMode(0);
                    client.getCredentials().signRequest(request, 0L);
                }

                ExecutionEngine.processRequest(request, opContext, this.getResult());

                if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_NO_CONTENT) {
                    this.setNonExceptionedRetryableFailure(true);
                    return null;
                }

                message.popReceipt = request.getHeaderField("x-ms-popreceipt");
                message.nextVisibleTime = Utility.parseRFC1123DateFromStringInGMT(request
                        .getHeaderField("x-ms-time-next-visible"));

                return null;
            }
        };

        ExecutionEngine.executeWithRetry(this.queueServiceClient, this, impl, options.getRetryPolicyFactory(),
                opContext);
    }

    /**
     * Uploads the metadata in the CloudQueue object to the queue, using the default request options.
     * 
     * @throws StorageException
     *             If a storage service error occurred during the operation.
     */
    @DoesServiceRequest
    public void uploadMetadata() throws StorageException {
        this.uploadMetadata(null, null);
    }

    /**
     * Uploads the metadata in the CloudQueue object to the queue, using the specified request options and
     * operation context.
     * 
     * @param options
     *            A {@link QueueRequestOptions} object that specifies any additional options for the request. Specifying
     *            null will use the default request options from the associated service client (
     *            {@link CloudQueueClient}).
     * 
     * @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 during the operation.
     */
    @DoesServiceRequest
    public void uploadMetadata(QueueRequestOptions options, OperationContext opContext) throws StorageException {
        if (opContext == null) {
            opContext = new OperationContext();
        }

        if (options == null) {
            options = new QueueRequestOptions();
        }

        opContext.initialize();
        options.applyDefaults(this.queueServiceClient);

        final StorageOperation impl = new StorageOperation(
                options) {

            @Override
            public Void execute(final CloudQueueClient client, final CloudQueue queue, final OperationContext opContext)
                    throws Exception {

                final HttpURLConnection request = QueueRequest.setMetadata(queue.getTransformedAddress(opContext), this
                        .getRequestOptions().getTimeoutIntervalInMs(), opContext);

                QueueRequest.addMetadata(request, queue.metadata, opContext);
                client.getCredentials().signRequest(request, 0L);

                ExecutionEngine.processRequest(request, opContext, this.getResult());

                if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_NO_CONTENT) {
                    this.setNonExceptionedRetryableFailure(true);
                }

                return null;
            }
        };

        ExecutionEngine.executeWithRetry(this.queueServiceClient, this, impl, options.getRetryPolicyFactory(),
                opContext);

    }

    /**
     * Uploads the queue's permissions.
     * 
     * @param permissions
     *            A {@link QueuePermissions} object that represents the permissions to upload.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public void uploadPermissions(final QueuePermissions permissions) throws StorageException {
        this.uploadPermissions(permissions, null, null);
    }

    /**
     * Uploads the queue's permissions using the specified request options and operation context.
     * 
     * @param permissions
     *            A {@link QueuePermissions} object that represents the permissions to upload.
     * @param options
     *            A {@link QueueRequestOptions} object that specifies any additional options for the request. Specifying
     *            null will use the default request options from the associated service client (
     *            {@link CloudQueueClient}).
     * @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 uploadPermissions(final QueuePermissions permissions, QueueRequestOptions options,
            OperationContext opContext) throws StorageException {
        if (opContext == null) {
            opContext = new OperationContext();
        }

        if (options == null) {
            options = new QueueRequestOptions();
        }

        opContext.initialize();
        options.applyDefaults(this.queueServiceClient);

        final StorageOperation impl = new StorageOperation(
                options) {

            @Override
            public Void execute(final CloudQueueClient client, final CloudQueue queue, final OperationContext opContext)
                    throws Exception {

                final HttpURLConnection request = QueueRequest.setAcl(queue.getTransformedAddress(opContext), this
                        .getRequestOptions().getTimeoutIntervalInMs(), opContext);

                final StringWriter outBuffer = new StringWriter();

                QueueRequest.writeSharedAccessIdentifiersToStream(permissions.getSharedAccessPolicies(), outBuffer);

                final byte[] aclBytes = outBuffer.toString().getBytes("UTF8");
                client.getCredentials().signRequest(request, aclBytes.length);
                final OutputStream outStreamRef = request.getOutputStream();
                outStreamRef.write(aclBytes);

                ExecutionEngine.processRequest(request, opContext, this.getResult());

                if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_NO_CONTENT) {
                    this.setNonExceptionedRetryableFailure(true);
                }

                return null;
            }
        };

        ExecutionEngine.executeWithRetry(this.queueServiceClient, this, impl, options.getRetryPolicyFactory(),
                opContext);
    }

    /**
     * Downloads the permission settings for the queue.
     * 
     * @return A {@link QueuePermissions} object that represents the queue's permissions.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public QueuePermissions downloadPermissions() throws StorageException {
        return this.downloadPermissions(null, null);
    }

    /**
     * Downloads the permissions settings for the queue using the specified request options and operation context.
     * 
     * @param options
     *            A {@link QueueRequestOptions} object that specifies any additional options for the request. Specifying
     *            null will use the default request options from the associated service client (
     *            {@link CloudQueueClient}).
     * @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 {@link QueuePermissions} object that represents the container's permissions.
     * 
     * @throws StorageException
     *             If a storage service error occurred.
     */
    @DoesServiceRequest
    public QueuePermissions downloadPermissions(QueueRequestOptions options, OperationContext opContext)
            throws StorageException {
        if (opContext == null) {
            opContext = new OperationContext();
        }

        if (options == null) {
            options = new QueueRequestOptions();
        }

        opContext.initialize();
        options.applyDefaults(this.queueServiceClient);

        final StorageOperation impl = new StorageOperation(
                options) {

            @Override
            public QueuePermissions execute(final CloudQueueClient client, final CloudQueue queue,
                    final OperationContext opContext) throws Exception {

                final HttpURLConnection request = QueueRequest.getAcl(queue.getTransformedAddress(opContext), this
                        .getRequestOptions().getTimeoutIntervalInMs(), opContext);

                client.getCredentials().signRequest(request, -1L);

                ExecutionEngine.processRequest(request, opContext, this.getResult());

                if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_OK) {
                    this.setNonExceptionedRetryableFailure(true);
                }

                final QueuePermissions permissions = new QueuePermissions();
                final QueueAccessPolicyResponse response = new QueueAccessPolicyResponse(request.getInputStream());

                for (final String key : response.getAccessIdentifiers().keySet()) {
                    permissions.getSharedAccessPolicies().put(key, response.getAccessIdentifiers().get(key));
                }

                return permissions;
            }
        };

        return ExecutionEngine.executeWithRetry(this.queueServiceClient, this, impl, options.getRetryPolicyFactory(),
                opContext);
    }

    /**
     * Returns a shared access signature for the queue.
     * 
     * @param policy
     *            The access policy for the shared access signature.
     * @param groupPolicyIdentifier
     *            A queue-level access policy.
     * @return A shared access signature for the queue.
     * @throws InvalidKeyException
     *             If an invalid key was passed.
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws IllegalArgumentException
     *             If an unexpected value is passed.
     */
    public String generateSharedAccessSignature(final SharedAccessQueuePolicy policy, final String groupPolicyIdentifier)
            throws InvalidKeyException, StorageException {

        if (!this.queueServiceClient.getCredentials().canCredentialsSignRequest()) {
            final String errorMessage = "Cannot create Shared Access Signature unless the Account Key credentials are used by the QueueServiceClient.";
            throw new IllegalArgumentException(errorMessage);
        }

        final String resourceName = this.getSharedAccessCanonicalName();

        final String signature = SharedAccessSignatureHelper.generateSharedAccessSignatureHash(policy,
                groupPolicyIdentifier, resourceName, this.queueServiceClient, null);

        final UriQueryBuilder builder = SharedAccessSignatureHelper.generateSharedAccessSignature(policy,
                groupPolicyIdentifier, signature);

        return builder.toString();
    }

    /**
     * Returns the canonical name for shared access.
     * 
     * @return the canonical name for shared access.
     */
    private String getSharedAccessCanonicalName() {
        if (this.queueServiceClient.isUsePathStyleUris()) {
            return this.getUri().getPath();
        }
        else {
            return PathUtility.getCanonicalPathFromCredentials(this.queueServiceClient.getCredentials(), this.getUri()
                    .getPath());
        }
    }

    /**
     * Returns the transformed URI for the resource if the given credentials require transformation.
     * 
     * @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 java.net.URI object that represents the transformed URI.
     * 
     * @throws IllegalArgumentException
     *             If the URI is not absolute.
     * @throws StorageException
     *             If a storage service error occurred.
     * @throws URISyntaxException
     *             If the resource URI is invalid.
     */
    protected final URI getTransformedAddress(final OperationContext opContext) throws URISyntaxException,
            StorageException {
        if (this.queueServiceClient.getCredentials().doCredentialsNeedTransformUri()) {
            if (this.getUri().isAbsolute()) {
                return this.queueServiceClient.getCredentials().transformUri(this.getUri(), opContext);
            }
            else {
                final StorageException ex = Utility.generateNewUnexpectedStorageException(null);
                ex.getExtendedErrorInformation().setErrorMessage("Queue Object relative URIs not supported.");
                throw ex;
            }
        }
        else {
            return this.getUri();
        }
    }

    /**
     * Parse Uri for SAS (Shared access signature) information.
     * 
     * Validate that no other query parameters are passed in. Any SAS information will be recorded as corresponding
     * credentials instance. If existingClient is passed in, any SAS information found will not be supported. Otherwise
     * a new client is created based on SAS information or as anonymous credentials.
     * 
     * @param completeUri
     *            The complete Uri.
     * @param existingClient
     *            The client to use.
     * @param usePathStyleUris
     *            If true, path style Uris are used.
     * @throws URISyntaxException
     * @throws StorageException
     */
    private void parseQueryAndVerify(final URI completeUri, final CloudQueueClient existingClient,
            final boolean usePathStyleUris) throws URISyntaxException, StorageException {
        Utility.assertNotNull("completeUri", completeUri);

        if (!completeUri.isAbsolute()) {
            final String errorMessage = String.format(
                    "Address '%s' is not an absolute address. Relative addresses are not permitted in here.",
                    completeUri.toString());
            throw new IllegalArgumentException(errorMessage);
        }

        this.uri = PathUtility.stripURIQueryAndFragment(completeUri);

        final HashMap queryParameters = PathUtility.parseQueryString(completeUri.getQuery());
        final StorageCredentialsSharedAccessSignature sasCreds = SharedAccessSignatureHelper
                .parseQuery(queryParameters);

        if (sasCreds == null) {
            return;
        }

        final Boolean sameCredentials = existingClient == null ? false : Utility.areCredentialsEqual(sasCreds,
                existingClient.getCredentials());

        if (existingClient == null || !sameCredentials) {
            this.queueServiceClient = new CloudQueueClient(new URI(PathUtility.getServiceClientBaseAddress(
                    this.getUri(), usePathStyleUris)), sasCreds);
        }

        if (existingClient != null && !sameCredentials) {
            this.queueServiceClient.setRetryPolicyFactory(existingClient.getRetryPolicyFactory());
            this.queueServiceClient.setTimeoutInMs(existingClient.getTimeoutInMs());
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy