1
# Copyright (c) 2006,2007 Mitch Garnaat http://garnaat.org/
3
# Permission is hereby granted, free of charge, to any person obtaining a
4
# copy of this software and associated documentation files (the
5
# "Software"), to deal in the Software without restriction, including
6
# without limitation the rights to use, copy, modify, merge, publish, dis-
7
# tribute, sublicense, and/or sell copies of the Software, and to permit
8
# persons to whom the Software is furnished to do so, subject to the fol-
11
# The above copyright notice and this permission notice shall be included
12
# in all copies or substantial portions of the Software.
14
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
16
# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
17
# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
18
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
25
A Message represents the data stored in an SQS queue. The rules for what is allowed within an SQS
28
http://docs.amazonwebservices.com/AWSSimpleQueueService/2008-01-01/SQSDeveloperGuide/Query_QuerySendMessage.html
30
So, at it's simplest level a Message just needs to allow a developer to store bytes in it and get the bytes
31
back out. However, to allow messages to have richer semantics, the Message class must support the
34
The constructor for the Message class must accept a keyword parameter "queue" which is an instance of a
35
boto Queue object and represents the queue that the message will be stored in. The default value for
36
this parameter is None.
38
The constructor for the Message class must accept a keyword parameter "body" which represents the
39
content or body of the message. The format of this parameter will depend on the behavior of the
40
particular Message subclass. For example, if the Message subclass provides dictionary-like behavior to the
41
user the body passed to the constructor should be a dict-like object that can be used to populate
42
the initial state of the message.
44
The Message class must provide an encode method that accepts a value of the same type as the body
45
parameter of the constructor and returns a string of characters that are able to be stored in an
46
SQS message body (see rules above).
48
The Message class must provide a decode method that accepts a string of characters that can be
49
stored (and probably were stored!) in an SQS message and return an object of a type that is consistent
50
with the "body" parameter accepted on the class constructor.
52
The Message class must provide a __len__ method that will return the size of the encoded message
53
that would be stored in SQS based on the current state of the Message object.
55
The Message class must provide a get_body method that will return the body of the message in the
56
same format accepted in the constructor of the class.
58
The Message class must provide a set_body method that accepts a message body in the same format
59
accepted by the constructor of the class. This method should alter to the internal state of the
60
Message object to reflect the state represented in the message body parameter.
62
The Message class must provide a get_body_encoded method that returns the current body of the message
63
in the format in which it would be stored in SQS.
68
from boto.sqs.attributes import Attributes
69
from boto.exception import SQSDecodeError
73
Base class for SQS messages. RawMessage does not encode the message
74
in any way. Whatever you store in the body of the message is what
75
will be written to SQS and whatever is returned from SQS is stored
76
directly into the body of the message.
79
def __init__(self, queue=None, body=''):
83
self.receipt_handle = None
85
self.attributes = Attributes(self)
88
return len(self.encode(self._body))
90
def startElement(self, name, attrs, connection):
91
if name == 'Attribute':
92
return self.attributes
95
def endElement(self, name, value, connection):
97
self.set_body(self.decode(value))
98
elif name == 'MessageId':
100
elif name == 'ReceiptHandle':
101
self.receipt_handle = value
102
elif name == 'MD5OfMessageBody':
105
setattr(self, name, value)
107
def encode(self, value):
108
"""Transform body object into serialized byte array format."""
111
def decode(self, value):
112
"""Transform seralized byte array into any object."""
115
def set_body(self, body):
116
"""Override the current body for this object, using decoded format."""
122
def get_body_encoded(self):
124
This method is really a semi-private method used by the Queue.write
125
method when writing the contents of the message to SQS.
126
You probably shouldn't need to call this method in the normal course of events.
128
return self.encode(self.get_body())
132
return self.queue.delete_message(self)
134
def change_visibility(self, visibility_timeout):
136
self.queue.connection.change_message_visibility(self.queue,
140
class Message(RawMessage):
142
The default Message class used for SQS queues. This class automatically
143
encodes/decodes the message body using Base64 encoding to avoid any
144
illegal characters in the message body. See:
146
http://developer.amazonwebservices.com/connect/thread.jspa?messageID=49680%EC%88%90
148
for details on why this is a good idea. The encode/decode is meant to
149
be transparent to the end-user.
152
def encode(self, value):
153
return base64.b64encode(value)
155
def decode(self, value):
157
value = base64.b64decode(value)
159
raise SQSDecodeError('Unable to decode message', self)
162
class MHMessage(Message):
164
The MHMessage class provides a message that provides RFC821-like
167
HeaderName: HeaderValue
169
The encoding/decoding of this is handled automatically and after
170
the message body has been read, the message instance can be treated
171
like a mapping object, i.e. m['HeaderName'] would return 'HeaderValue'.
174
def __init__(self, queue=None, body=None, xml_attrs=None):
175
if body == None or body == '':
177
Message.__init__(self, queue, body)
179
def decode(self, value):
182
fp = StringIO.StringIO(value)
185
delim = line.find(':')
187
value = line[delim+1:].strip()
188
msg[key.strip()] = value.strip()
191
raise SQSDecodeError('Unable to decode message', self)
194
def encode(self, value):
196
for item in value.items():
197
s = s + '%s: %s\n' % (item[0], item[1])
200
def __getitem__(self, key):
201
if self._body.has_key(key):
202
return self._body[key]
206
def __setitem__(self, key, value):
207
self._body[key] = value
208
self.set_body(self._body)
211
return self._body.keys()
214
return self._body.values()
217
return self._body.items()
219
def has_key(self, key):
220
return self._body.has_key(key)
224
self.set_body(self._body)
226
def get(self, key, default=None):
227
return self._body.get(key, default)
229
class EncodedMHMessage(MHMessage):
231
The EncodedMHMessage class provides a message that provides RFC821-like
234
HeaderName: HeaderValue
236
This variation encodes/decodes the body of the message in base64 automatically.
237
The message instance can be treated like a mapping object,
238
i.e. m['HeaderName'] would return 'HeaderValue'.
241
def decode(self, value):
243
value = base64.b64decode(value)
245
raise SQSDecodeError('Unable to decode message', self)
246
return MHMessage.decode(self, value)
248
def encode(self, value):
249
value = MHMessage.encode(value)
250
return base64.b64encode(self, value)