~vishvananda/nova/network-refactor

« back to all changes in this revision

Viewing changes to vendor/boto/boto/sqs/message.py

  • Committer: Jesse Andrews
  • Date: 2010-05-28 06:05:26 UTC
  • Revision ID: git-v1:bf6e6e718cdc7488e2da87b21e258ccc065fe499
initial commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (c) 2006,2007 Mitch Garnaat http://garnaat.org/
 
2
#
 
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-
 
9
# lowing conditions:
 
10
#
 
11
# The above copyright notice and this permission notice shall be included
 
12
# in all copies or substantial portions of the Software.
 
13
#
 
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
 
20
# IN THE SOFTWARE.
 
21
 
 
22
"""
 
23
SQS Message
 
24
 
 
25
A Message represents the data stored in an SQS queue.  The rules for what is allowed within an SQS
 
26
Message are here:
 
27
 
 
28
    http://docs.amazonwebservices.com/AWSSimpleQueueService/2008-01-01/SQSDeveloperGuide/Query_QuerySendMessage.html
 
29
 
 
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 
 
32
following interfaces:
 
33
 
 
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.
 
37
 
 
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.
 
43
 
 
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).
 
47
 
 
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.
 
51
 
 
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.
 
54
 
 
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.
 
57
 
 
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.
 
61
 
 
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.
 
64
"""
 
65
 
 
66
import base64
 
67
import StringIO
 
68
from boto.sqs.attributes import Attributes
 
69
from boto.exception import SQSDecodeError
 
70
 
 
71
class RawMessage:
 
72
    """
 
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.
 
77
    """
 
78
    
 
79
    def __init__(self, queue=None, body=''):
 
80
        self.queue = queue
 
81
        self.set_body(body)
 
82
        self.id = None
 
83
        self.receipt_handle = None
 
84
        self.md5 = None
 
85
        self.attributes = Attributes(self)
 
86
 
 
87
    def __len__(self):
 
88
        return len(self.encode(self._body))
 
89
 
 
90
    def startElement(self, name, attrs, connection):
 
91
        if name == 'Attribute':
 
92
            return self.attributes
 
93
        return None
 
94
 
 
95
    def endElement(self, name, value, connection):
 
96
        if name == 'Body':
 
97
            self.set_body(self.decode(value))
 
98
        elif name == 'MessageId':
 
99
            self.id = value
 
100
        elif name == 'ReceiptHandle':
 
101
            self.receipt_handle = value
 
102
        elif name == 'MD5OfMessageBody':
 
103
            self.md5 = value
 
104
        else:
 
105
            setattr(self, name, value)
 
106
 
 
107
    def encode(self, value):
 
108
        """Transform body object into serialized byte array format."""
 
109
        return value
 
110
 
 
111
    def decode(self, value):
 
112
        """Transform seralized byte array into any object."""
 
113
        return value
 
114
 
 
115
    def set_body(self, body):
 
116
        """Override the current body for this object, using decoded format."""
 
117
        self._body = body
 
118
 
 
119
    def get_body(self):
 
120
        return self._body
 
121
    
 
122
    def get_body_encoded(self):
 
123
        """
 
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.
 
127
        """
 
128
        return self.encode(self.get_body())
 
129
 
 
130
    def delete(self):
 
131
        if self.queue:
 
132
            return self.queue.delete_message(self)
 
133
 
 
134
    def change_visibility(self, visibility_timeout):
 
135
        if self.queue:
 
136
            self.queue.connection.change_message_visibility(self.queue,
 
137
                                                            self.receipt_handle,
 
138
                                                            visibility_timeout)
 
139
    
 
140
class Message(RawMessage):
 
141
    """
 
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:
 
145
 
 
146
    http://developer.amazonwebservices.com/connect/thread.jspa?messageID=49680%EC%88%90
 
147
 
 
148
    for details on why this is a good idea.  The encode/decode is meant to
 
149
    be transparent to the end-user.
 
150
    """
 
151
    
 
152
    def encode(self, value):
 
153
        return base64.b64encode(value)
 
154
 
 
155
    def decode(self, value):
 
156
        try:
 
157
            value = base64.b64decode(value)
 
158
        except:
 
159
            raise SQSDecodeError('Unable to decode message', self)
 
160
        return value
 
161
 
 
162
class MHMessage(Message):
 
163
    """
 
164
    The MHMessage class provides a message that provides RFC821-like
 
165
    headers like this:
 
166
 
 
167
    HeaderName: HeaderValue
 
168
 
 
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'.
 
172
    """
 
173
 
 
174
    def __init__(self, queue=None, body=None, xml_attrs=None):
 
175
        if body == None or body == '':
 
176
            body = {}
 
177
        Message.__init__(self, queue, body)
 
178
 
 
179
    def decode(self, value):
 
180
        try:
 
181
            msg = {}
 
182
            fp = StringIO.StringIO(value)
 
183
            line = fp.readline()
 
184
            while line:
 
185
                delim = line.find(':')
 
186
                key = line[0:delim]
 
187
                value = line[delim+1:].strip()
 
188
                msg[key.strip()] = value.strip()
 
189
                line = fp.readline()
 
190
        except:
 
191
            raise SQSDecodeError('Unable to decode message', self)
 
192
        return msg
 
193
 
 
194
    def encode(self, value):
 
195
        s = ''
 
196
        for item in value.items():
 
197
            s = s + '%s: %s\n' % (item[0], item[1])
 
198
        return s
 
199
 
 
200
    def __getitem__(self, key):
 
201
        if self._body.has_key(key):
 
202
            return self._body[key]
 
203
        else:
 
204
            raise KeyError(key)
 
205
 
 
206
    def __setitem__(self, key, value):
 
207
        self._body[key] = value
 
208
        self.set_body(self._body)
 
209
 
 
210
    def keys(self):
 
211
        return self._body.keys()
 
212
 
 
213
    def values(self):
 
214
        return self._body.values()
 
215
 
 
216
    def items(self):
 
217
        return self._body.items()
 
218
 
 
219
    def has_key(self, key):
 
220
        return self._body.has_key(key)
 
221
 
 
222
    def update(self, d):
 
223
        self._body.update(d)
 
224
        self.set_body(self._body)
 
225
 
 
226
    def get(self, key, default=None):
 
227
        return self._body.get(key, default)
 
228
 
 
229
class EncodedMHMessage(MHMessage):
 
230
    """
 
231
    The EncodedMHMessage class provides a message that provides RFC821-like
 
232
    headers like this:
 
233
 
 
234
    HeaderName: HeaderValue
 
235
 
 
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'.
 
239
    """
 
240
 
 
241
    def decode(self, value):
 
242
        try:
 
243
            value = base64.b64decode(value)
 
244
        except:
 
245
            raise SQSDecodeError('Unable to decode message', self)
 
246
        return MHMessage.decode(self, value)
 
247
 
 
248
    def encode(self, value):
 
249
        value = MHMessage.encode(value)
 
250
        return base64.b64encode(self, value)
 
251