Making a web service request includes:
Configuring the Sample
Properly authenticating your request
Implementing SQS Actions
The examples in this guide demonstrate how to use common SQS actions in a simple command-line tool. The command-line tool takes input parameters and performs a web service request to SQS, using the inut values as parameters in the request. Using the sample you can create a queue, send a message, retrieve and delete a message, and list your existing queues.
Before compiling and running the provided sample applications, you need to include your own Access identifiers.
The Java sample uses your Access identifiers to calculate an HMAC-SHA1 hash, which is included in your request as the signature. Insert your AWS Access Key ID and Secret Access Key where specified. Your Secret Access key is used to create a signature for the request. The value of the key itself it not send within the request. The code is the same for both samples files, so only one is displayed here. Be sure to insert your AWS Access Key ID and Secret Access Key in both of the sample files, EnqueueSample.java and DequeueSample.java.
public class EnqueueSample {
private static Logger log = null;
private static final String DEFAULT_SERVICE_URL = "http://queue.amazonaws.com";
//------------------------------------------------------------------------
public static void main( String[] args ) {
final String AWSAccessKeyId = "";
final String SecretAccessKey = "";Create a QueueService object and a MessageQueue object.
// Create a QueueService object, a Queue
QueueService service = getQueueService( url );
// Create the Message Queue object
MessageQueue msgQueue = getMessageQueue( service, queueName );
String msgId = msgQueue.sendMessage( message );When using SOAP requests to AWS, you can altenatively use an X.509 certificate as an Access identifier for request authentication. The certificate must be uploaded to AWS before you it can be used to authenticate a request. For more information about how to obtain or upload a certificate for use with your AWS account, please see the View Access Identifiers page.
# Add your own keys next my $ACCESS_KEY_ID = 'WRITE YOUR OWN AWS ACCESS_KEY_ID HERE'; my $SECRET_ACCESS_KEY_ID = 'WRITE YOUR OWN SECRET_ACCESS_KEY_ID'; # Remove these next two lines as well, when you've updated your Access Identifiers. print "update QueueServiceMethods.pm with your AWS Access Identifiers\n"; exit;
Authentication allows you to identify your application as the sender of the web service request. Your request must include either an a signature, or an X.509 certificate. There are samples demonstrating each type of authentication in this Getting Started Guide.
The AWS Access Key ID is a public identifier you receive when you get a developer account. Request signatures are calculated using the parameters included in the request, and your AWS Access Key ID and Secret Access Key (also generated upon creating an AWS account).
The signature is encrypted using both an keyed-hash message authentication code (HMAC) algorithm and a Base 64 encoding algorithm.
Note: | For REST requests, be sure to also URL encode the signature, as Base-64 encoding usually produces special characters. |
The following code snippet creates a signature for the request. The signature is included in the SOAP request sent to AWS. The code is the same in both sample files, so only one has been included here.
// Authenticating the Request SignRequestHandlerHMAC.setWSSecurityInfo(AWSAccessKeyId, SecretAccessKey);
The C# samples uses SOAP to form and send requests to AWS. Authentication in the C# sample is done using WSE to form a WS-Security block. Because this is done automatically by the tools, no sample code is provided.
Authentication for the Perl sample is done by using your AWS Access Key ID and Secret Access Key to calculate a request signature. Code for using both REST and HTTP Query is included. The following snippet demonstrates constructing a Query request including the parameters used for authentication.
# build query string
my $security_params = get_security_params($rest_or_query, 'CreateQueue', 'EXPIRES');
my $param_string;
for my $field_name (keys %{$param_hash_ref}) {
$param_string .= "&" . $field_name . "=" . $param_hash_ref->{$field_name};
}
my $query_string = "/?Action=CreateQueue&Version=2006-04-01" . $param_string . $security_params;Here is the snippet that demonstrates constructing a REST request (to create a queue) including the parameters used for authentication.
# get security headers ('POST' = CreateQueue)
my $security_params = get_security_params($rest_or_query, 'POST', 'EXPIRES', $service_url, $param_hash_ref);
# define query_string
my ($query_string, $param_string, @param_string_array);
for my $param (keys(%{$param_hash_ref})) {
push(@param_string_array, $param . '=' . $param_hash_ref->{$param});
}
if (@param_string_array) {
$query_string .= '/?';
$param_string = join('&', @param_string_array);
$query_string .= $param_string;
}
my $rest_results = $rest_client->make_query(
$service_url,
$query_string,
'POST',
$param_hash_ref,
$security_params
);The following snippet from function get_security_params calculates the signature for a Query request:
my $rest_or_query = shift;
my $action = shift;
my $now_or_expires = shift;
my $service_url = shift;
my $param_hash_ref = shift;
my $timestamp = get_timestamp($now_or_expires);
my $signature_key = $action . $timestamp;
my $signature = get_hmac_signature($signature_key, $SECRET_ACCESS_KEY_ID, $rest_or_query);
my $security_params;
if ($rest_or_query =~ /QUERY/i) {
my $timestamp = get_timestamp($now_or_expires);
my $signature_key = $action . $timestamp;
my $signature = get_hmac_signature($signature_key, $SECRET_ACCESS_KEY_ID, $rest_or_query);
$security_params .= "&AWSAccessKeyId=" . $ACCESS_KEY_ID;
$security_params .= ("&Timestamp=" . $timestamp) if ($now_or_expires =~ /NOW/i);
$security_params .= ("&Expires=" . $timestamp) if ($now_or_expires =~ /EXPIRES/i);
$security_params .= "&Signature=" . $signature;
}This snippet from the same function demonstrates performing authentication using REST.
# REST signature
elsif ($rest_or_query =~ /REST/i) {
# timestamp needs to look like 'Thu, 17 Nov 2005 18:49:58 GMT'
my $time_in_seconds = time;
my $expires_time = $time_in_seconds;
my $preformatted_timestamp = scalar(gmtime($expires_time));
# timestamp parse
# $1 = day of week
# $2 = month
# $3 = mday
# $4 = time
# $5 = year
$preformatted_timestamp =~ /^(\w+)\s+(\w+)\s+(\d+)\s+(\d+\:\d+\:\d+)\s+(\d+)$/;
my $timestamp = sprintf("%3s, %02d %3s %8s %4s GMT", $1, $3, $2, $5, $4);
# store params in a hash, if they're passed in, convert to string for md5
my $param_string;
my $md5_signature;
# start string to be hmac'ed with the action
my $canonical_string = $action . "\n";
# add a '\n' to cover the absence of the md5
$canonical_string .= "\n";
# add the content type
my $content_type = 'text/plain';
$canonical_string .= $content_type . "\n";
# add the date
$canonical_string .= $timestamp. "\n";
# truncate and add the service resource
chomp $service_url;
if ($service_url =~ /^http:\/\/.*?(\/.*?)\/?\??$/) {
$canonical_string .= $1;
}
$canonical_string .= '/';
# sign the canonical string
# print "Signing string: $canonical_string\n";
my $signed_canonical_string = get_hmac_signature($canonical_string, $SECRET_ACCESS_KEY_ID, $rest_or_query);
$security_params->{'md5_signature'} = $md5_signature;
$security_params->{'signed_canonical_string'} = $signed_canonical_string;
$security_params->{'date'} = $timestamp;
$security_params->{'content_type'} = $content_type;
$security_params->{'access_key_id'} = $ACCESS_KEY_ID;
$security_params->{'secret_access_key_id'} = $SECRET_ACCESS_KEY_ID;
# use in 'Authorization' header
$security_params->{'auth_string'} = "AWS $ACCESS_KEY_ID" . ':' . $signed_canonical_string;
}This function generates the Timestamp in the correct format.
# get_timestamp()
# returns string of timestamp
sub get_timestamp {
my $now_or_expires = shift;
my $time_string;
if ($now_or_expires =~ /NOW/i) {
my @time = (gmtime(time));
my $time_string = sprintf("%04d-%02d-%02dT%02d:%02d:%02dZ",
$time[5] + 1900,
$time[4] + 1,
$time[3],
$time[2],
$time[1],
$time[0]
);
return $time_string;
} elsif ($now_or_expires =~ /EXPIRES/i) {
my @time = (gmtime(time + 900));
my $time_string = sprintf("%04d-%02d-%02dT%02d:%02d:%02dZ",
$time[5] + 1900,
$time[4] + 1,
$time[3],
$time[2],
$time[1],
$time[0]
);
return $time_string;
}
}This snippet demonstrates performing encoding on the signature values.
# get_hmac_signature(string $data, string $key)
# returns hmac sig for key
sub get_hmac_signature {
use Digest::HMAC_SHA1 qw(hmac_sha1);
use MIME::Base64 qw(encode_base64);
my $data = shift;
my $key = shift;
my $rest_or_query = shift;
my $hmac = hmac_sha1($data, $key);
chomp $hmac;
my $encoded_hmac = encode_base64($hmac);
chomp $encoded_hmac;
# it turns out that the Query sig needs to be encoded, but the Rest fails if it is
if ($rest_or_query =~ /QUERY/i) {
my $url_encoded_hmac = url_encode($encoded_hmac);
chomp $url_encoded_hmac;
return $url_encoded_hmac;
} else {
return $encoded_hmac;
}
}
sub get_md5_signature {
use Digest::MD5 qw(md5_base64);
my $data = shift;
return md5_base64($data);
}
sub url_encode {
my $url = shift;
$url =~ s/([\W])/"%" . uc(sprintf("%2.2x",ord($1)))/eg;
return $url;
}The code to create a queue makes the actual web service request and capture the response for processing. Here's the code snippet from our example that does this:
/**
* Create a Queue
*/
public static QueueService getQueueService( String url )
throws MalformedURLException, AxisFault {
log.info( "Init QueueService" );
QueueService service = new QueueService( url );
if ( service.getUrl() != null ) {
log.info( "Service: " + service.getUrl().toString() );
} else {
log.info( "Service: null url!" );
}
return service;
}/**
* Returns the URL for a queue with the name provided.
* If the queue doesn't exist, it will create it.
*/
public static string createQueue(string queueName)
{
//The queue service for
com.amazonaws.queue.QueueService queueService = new com.amazonaws.queue.QueueService();
queueService.Url = Client.sqsUrl;
queueService.SetPolicy(Client.sqsPolicy);
com.amazonaws.queue.CreateQueue createQueue = new com.amazonaws.queue.CreateQueue();
createQueue.QueueName = queueName;
com.amazonaws.queue.CreateQueueResponse response = queueService.CreateQueue(createQueue);
//do we need error checking on the response here?
return response.QueueUrl;
}# returns Queue URL
sub createQueue {
my $service_url = shift;
my $rest_or_query = shift;
my $param_hash_ref = shift;
if ((!$service_url) || (!$rest_or_query)) {
return 0;
}
if ($rest_or_query =~ /QUERY/i) {
my $query_client = QueryAccessXinoXPath->new();
# build query string
my $security_params = get_security_params($rest_or_query, 'CreateQueue', 'EXPIRES');
my $param_string;
for my $field_name (keys %{$param_hash_ref}) {
$param_string .= "&" . $field_name . "=" . $param_hash_ref->{$field_name};
}
my $query_string = "/?Action=CreateQueue&Version=2006-04-01" . $param_string . $security_params;
# make request. Results come back as an XPath object
my $query_results = $query_client->make_query(
$service_url,
$query_string,
);
unless ($query_results) {
return 0;
} else {
return $query_results;
}The code to create a queue makes the actual web service request and captures the response for processing. Here's the code snippet from our example that does this:
/**
* Creates a message to send to the queue, and returns a MessageQueue object.
*/
public static MessageQueue getMessageQueue( QueueService service, String msgQueueName )
throws MalformedURLException, RemoteException, QueueServiceException {
log.info( "Creating message queue "
+ msgQueueName
+ " (although it may already exist)" );
MessageQueue msgQueue = service.getOrCreateMessageQueue( msgQueueName );
if ( msgQueue == null ) {
log.error( "Message queue couldn't be created" );
}
return msgQueue;
}/**
* Enqueue the provided string message onto the queue located at url.
* Returns the message id.
*/
public static string sendMessage(string url, string message)
{
com.amazonaws.queue.MessageQueue messageQueue = new com.amazonaws.queue.MessageQueue();
messageQueue.Url = url;
messageQueue.SetPolicy(Client.sqsPolicy);
com.amazonaws.queue.SendMessage sendMessage = new com.amazonaws.queue.SendMessage();
sendMessage.MessageBody = message;
com.amazonaws.queue.SendMessageResponse response = messageQueue.SendMessage(sendMessage);
//do we need error checking on the response here?
return response.MessageId;
}# sendMessage(string $service_url, string $rest_or_query, hash_ref $params)
sub sendMessage {
my $service_url = shift;
my $rest_or_query = shift;
my $param_hash_ref = shift;
if ((!$service_url) || (!$rest_or_query)) {
return 0;
}
if ($rest_or_query =~ /QUERY/i) {
my $rest_client = QueryAccessXinoXPath->new();
# build query string
my $security_params = get_security_params($rest_or_query, 'SendMessage', 'EXPIRES');
my $param_string;
for my $field_name (keys %{$param_hash_ref}) {
my $message_body_count = 1;
# MessageBodies is a compound field, need to break
# it down into single params
if ($field_name =~ /MessageBody/) {
# if the contents of this field are in an array,
# parse the array values into separate parameters
if (ref($param_hash_ref->{$field_name}) =~ /ARRAY/) {
for my $message_body (@{$param_hash_ref->{$field_name}}) {
$param_string .= "&MessageBody." . $message_body_count . "=" . $message_body;
$message_body_count++;
}
# otherwise, submit a single MessageBody param
} else {
$param_string .= "&MessageBody" . "=" . $param_hash_ref->{$field_name};
}
} else {
# Balance of the fields can just come in as single params
$param_string .= "&" . $field_name . "=" . $param_hash_ref->{$field_name};
}
}
my $query_string = "/?Action=SendMessage&Version=2006-04-01" . $security_params . $param_string;
# make request. Results come back as an XPath object
my $rest_query_results = $rest_client->make_query(
$service_url,
$query_string,
);
unless ($rest_query_results) {
return 0;
} else {
return $rest_query_results;
}
} elsif ($rest_or_query =~ /REST/i) {
require RestAccessXinoXPath;
my $rest_client = RestAccessXinoXPath->new();
# REST target for posts
$service_url .= '/back/';
# get security headers ('PUT' = SendMessage)
my $security_params = get_security_params($rest_or_query, 'PUT', 'EXPIRES', $service_url, $param_hash_ref);
# define request body that contains one or more messages
my $body_string;
for my $field_name (keys %{$param_hash_ref}) {
# MessageBodies is a compound field, need to break
# it down into single params
if ($field_name =~ /MessageBody/) {
# if the contents of this field are in an array,
# parse the array values into separate parameters
if (ref($param_hash_ref->{$field_name}) =~ /ARRAY/) {
for my $message_body (@{$param_hash_ref->{$field_name}}) {
$body_string .= $message_body;
}
# otherwise, submit a single MessageBody param
} else {
$body_string .= $param_hash_ref->{$field_name};
}
}
}
my $rest_results = $rest_client->make_query(
$service_url,
'',
'PUT',
$param_hash_ref,
$security_params,
$body_string
);
unless ($rest_results) {
return 0;
} else {
return $rest_results;
}
}
}The code for retrieving and then deleting a message from the queue is listed below. Due to the distributed nature of the queue, multiple attempts may be required to receive a message from the queue. Here we make up to 5 attempts before determining that the queue is empty.
// Try to retrieve (dequeue) a message, and then delete it.
// Retry at most four times if unsuccessful.
for (int i = 1; i <= 5; i++) {
Message msg = msgQueue.receiveMessage();
if ( msg == null ) {
log.info( "Try # " + Integer.toString(i) + ": no messages received" );
// Sleep for 1 second before the next try
Thread.sleep(1000);
} else {
log.info( "Received message "
+ msg.getMessageId()
+ "={"
+ msg.getMessageBody()
+ "}"
);
msgQueue.setVisibilityTimeout( msg.getMessageId(), 360 );
log.info( "Set the message's receive visibility"
+ " timeout seconds to 360" );
Message msgCopy = msgQueue.peekMessage( msg.getMessageId() );
if ( msgCopy == null ) {
log.warn( "Couldn't peek message" );
} else {
log.info( "Peeked message "
+ msgCopy.getMessageId()
+ "={"
+ msgCopy.getMessageBody()
+ "}" );
}
msgQueue.deleteMessage( msg.getMessageId() );
log.info( "Deleted message id " + msg.getMessageId());
break;
}
}
} catch ( Exception ex ) {
log.error( "EXCEPTION", ex );
}
}//Retrieve a Message
try
{
for (int i = 0; i < 5; i++)
{
//locate the queue with the supplied name and check to make sure it exists
queueUrl = SimpleQueueServiceClient.Client.findQueue(queueName);
if (queueUrl == null)
{
//retry until the 5th try
if (i < 4)
{
Thread.Sleep(1000);
continue;
}
Console.Out.WriteLine("Error finding queue \"" + queueName + "\" located at " + queueUrl);
return;
}
//retrieve a message on the queue at the specified url
message = SimpleQueueServiceClient.Client.recieveMessage(queueUrl);
if (message == null)
{
//retry until the 5th try
if (i < 4)
{
Thread.Sleep(1000);
continue;
}
Console.Out.WriteLine("Error retrieving messages from queue \"" + queueName + "\" located at " + queueUrl);
return;
}
//remove the message off of the queue
string success = SimpleQueueServiceClient.Client.deleteMessage(queueUrl, message);
if (success == null)
{
//retry until the 5th try
if (i < 4)
{
Thread.Sleep(1000);
continue;
}
Console.Out.WriteLine("Error removing \"" + message.MessageBody + "\" id=" + message.MessageId + "\nfrom queue \"" + queueName + "\" located at " + queueUrl);
return;
}
else if (success == "Success")
break;
}
}
catch (System.Web.Services.Protocols.SoapException e)
{
Console.Out.WriteLine(e.Message);
return;
}
Console.Out.WriteLine("Dequeued \"" + message.MessageBody + "\" id=" + message.MessageId + "\nfrom \"" + queueName + "\" located at " + queueUrl);# receiveMessage(string $service_url, string $rest_or_query, hash_ref $params)
# returns Messages/Message array (each of which contains MessageId/MessageBody combo)
sub receiveMessage {
my $service_url = shift;
my $rest_or_query = shift;
my $param_hash_ref = shift;
if ((!$service_url) || (!$rest_or_query)) {
return 0;
}
if ($rest_or_query =~ /QUERY/i) {
my $rest_client = QueryAccessXinoXPath->new();
# build query string
my $security_params = get_security_params($rest_or_query, 'ReceiveMessage', 'EXPIRES');
my $param_string;
for my $field_name (keys %{$param_hash_ref}) {
$param_string .= "&" . $field_name . "=" . $param_hash_ref->{$field_name};
}
my $query_string = "/?Action=ReceiveMessage&Version=2006-04-01" . $security_params . $param_string;
# make request. Results come back as an XPath object
my $rest_query_results = $rest_client->make_query(
$service_url,
$query_string,
);
unless ($rest_query_results) {
return 0;
} else {
return $rest_query_results;
}
} elsif ($rest_or_query =~ /REST/i) {
require RestAccessXinoXPath;
my $rest_client = RestAccessXinoXPath->new();
# REST target for posts
$service_url .= '/front/';
# get security headers ('GET' = ReceiveMessage)
my $security_params = get_security_params($rest_or_query, 'GET', 'EXPIRES', $service_url, $param_hash_ref);
# define query_string
my ($query_string, $param_string, @param_string_array);
for my $param (keys(%{$param_hash_ref})) {
push(@param_string_array, $param . '=' . $param_hash_ref->{$param});
}
if (@param_string_array) {
$query_string .= '/?';
$param_string = join('&', @param_string_array);
$query_string .= $param_string;
}
my $rest_results = $rest_client->make_query(
$service_url,
$query_string,
'GET',
$param_hash_ref,
$security_params
);
unless ($rest_results) {
return 0;
} else {
return $rest_results;
}
}
}# deleteMessage(string $service_url, string $rest_or_query, hash_ref $params)
# returns success/failure
sub deleteMessage {
my $service_url = shift;
my $rest_or_query = shift;
my $param_hash_ref = shift;
if ((!$service_url) || (!$rest_or_query)) {
return 0;
}
if ($rest_or_query =~ /QUERY/i) {
my $rest_client = QueryAccessXinoXPath->new();
# build query string
my $security_params = get_security_params($rest_or_query, 'DeleteMessage', 'EXPIRES');
my $param_string;
for my $field_name (keys %{$param_hash_ref}) {
my $message_body_count = 1;
# MessageIds is a compound field, need to break
# it down into single params
if ($field_name =~ /MessageId/) {
# if the contents of this field are in an array,
# parse the array values into separate parameters
if (ref($param_hash_ref->{$field_name}) =~ /ARRAY/) {
for my $message_body (@{$param_hash_ref->{$field_name}}) {
$param_string .= "&MessageId." . $message_body_count . "=" . $message_body;
$message_body_count++;
}
# otherwise, submit a single MessageBody param
} else {
$param_string .= "&MessageId" . "=" . $param_hash_ref->{$field_name};
}
}
# Balance of the fields can just come in as single params
$param_string .= "&" . $field_name . "=" . $param_hash_ref->{$field_name};
}
my $query_string = "/?Action=DeleteMessage&Version=2006-04-01" . $security_params . $param_string;
# make request. Results come back as an XPath object
my $rest_query_results = $rest_client->make_query(
$service_url,
$query_string,
);
unless ($rest_query_results) {
return 0;
} else {
return $rest_query_results;
}
} elsif ($rest_or_query =~ /REST/i) {
require RestAccessXinoXPath;
my $rest_client = RestAccessXinoXPath->new();
# REST target for posts
$service_url .= '/' . $param_hash_ref->{'MessageId'} . '/';
# get security headers ('GET' = PeekMessage)
my $security_params = get_security_params($rest_or_query, 'DELETE', 'EXPIRES', $service_url, $param_hash_ref);
# define query_string
my ($query_string, $param_string, @param_string_array);
for my $param (keys(%{$param_hash_ref})) {
push(@param_string_array, $param . '=' . $param_hash_ref->{$param});
}
my $rest_results = $rest_client->make_query(
$service_url,
$query_string,
'DELETE',
$param_hash_ref,
$security_params
);
unless ($rest_results) {
return 0;
} else {
return $rest_results;
}
}
}