ruote-amqp 2.2.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,350 @@
1
+ #--
2
+ # Copyright (c) 2010-2012, Kenneth Kalmer, John Mettraux.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in
12
+ # all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, 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 IN
20
+ # THE SOFTWARE.
21
+ #++
22
+
23
+
24
+ module Ruote::Amqp
25
+
26
+ #
27
+ # This participant publishes messages on AMQP exchanges.
28
+ #
29
+ # == options
30
+ #
31
+ # A few options are supported. They can be declared at 3 levels:
32
+ #
33
+ # * options (when the participant is registered)
34
+ #
35
+ # dashboard.register(
36
+ # 'amqp_participant',
37
+ # Ruote::Amqp::Participant,
38
+ # :routing_key => 'nada.x')
39
+ #
40
+ # * params (from the process definition)
41
+ #
42
+ # sequence do
43
+ # amqp_participant :routing_key => 'nada.x'
44
+ # end
45
+ #
46
+ # * fields (from the passing workitem)
47
+ #
48
+ # sequence do
49
+ # set 'f:routing_key' => 'nada.x'
50
+ # amqp_participant
51
+ # end
52
+ #
53
+ # The 'conf' option (only available at participant registration) decides
54
+ # which levels are enabled or not.
55
+ #
56
+ # By default 'conf' is set to 'params, fields, options'.
57
+ #
58
+ # === 'conf'
59
+ #
60
+ # As said above, this option decides who can tweak this participant's
61
+ # options. It accepts a comma-separated list of levels.
62
+ #
63
+ # The levels are "params", "fields", "options".
64
+ #
65
+ # The order in which the levels are given is the order in which they
66
+ # are investigated for values.
67
+ #
68
+ # === 'connection'
69
+ #
70
+ # A hash of connection options. This is direcly fed to the amqp gem, the
71
+ # options of that gem apply thus ('host', 'port', 'vhost', 'username' and
72
+ # 'password').
73
+ #
74
+ # If no 'connection' (or :connection) hash is passed, the participant
75
+ # will attempt to use the connection (AMQP::Session) found in
76
+ # Ruote::Amqp.session. If there is nothing in there, it will [attempt] to
77
+ # create a new connection with AMQP's default settings.
78
+ #
79
+ # === 'exchange'
80
+ #
81
+ # Accepts a two or three sized Array.
82
+ #
83
+ # The first element is a string or symbol detailing the exchange type,
84
+ # like :direct, :fanout, :topic, ...
85
+ #
86
+ # The second element is an exchange name.
87
+ #
88
+ # The third, optional, element is a hash of exchange options.
89
+ #
90
+ # There is more information at http://rubyamqp.info/
91
+ #
92
+ # By default, 'exchange' is set to [ 'direct', '' ] (the default exchange).
93
+ #
94
+ # Note: you cannot pass an instantiated Ruby-AMQP exchange here. Ruote
95
+ # cannot serialize it for remote workers, so the settings are passed
96
+ # in a flat form, easily JSONifiable.
97
+ #
98
+ # === 'field_prefix'
99
+ #
100
+ # Sometimes one wants to separate his AMQP participant settings from other
101
+ # workitem fields.
102
+ #
103
+ # dashboard.register(
104
+ # 'amqp_participant',
105
+ # Ruote::Amqp::Participant,
106
+ # :conf => 'fields', :field_prefix => 'amqp_')
107
+ #
108
+ # registers a participant that draws is configuration from workitem fields
109
+ # prefixed with 'amqp_'.
110
+ #
111
+ # Note that setting this option doesn't implicitely add 'fields' to the
112
+ # 'conf' option.
113
+ #
114
+ # === 'forget'
115
+ #
116
+ # When set to true forces the participant to reply to the engine immediately
117
+ # after the message got published, in a "fire and forget" fashion.
118
+ #
119
+ # === 'routing_key'
120
+ #
121
+ # Depending on the exchange used, this option lets you influence how the
122
+ # exchange routes the message towards queues.
123
+ #
124
+ # Consult your AMQP documentation for more information.
125
+ #
126
+ # === 'message'
127
+ #
128
+ # By default, the workitem is turned into a JSON string and transmitted in
129
+ # the AMQP message payload. If this 'message' option is set, its value is
130
+ # used as the payload.
131
+ #
132
+ # === 'persistent'
133
+ #
134
+ # If this option is set to something else than false or nil, messages
135
+ # messages published by this participant will be persistent (hopefully
136
+ # the queues they'll end up in will be persistent as well).
137
+ #
138
+ #
139
+ # == #encode_workitem
140
+ #
141
+ # The default way to encode a workitem before pushing it to the exchange
142
+ # is by turning it entirely into a JSON string.
143
+ #
144
+ # To alter this, one can subclass this participant and provide its own
145
+ # #encode_workitem(wi) method:
146
+ #
147
+ # require 'yaml'
148
+ #
149
+ # class MyAmqpParticipant < Ruote::Amqp::Participant
150
+ #
151
+ # def encode_workitem(workitem)
152
+ # YAML.dump(workitem)
153
+ # end
154
+ # end
155
+ #
156
+ # or when one needs to filter some fields:
157
+ #
158
+ # class MyAmqpParticipant < Ruote::Amqp::Participant
159
+ #
160
+ # def encode_workitem(workitem)
161
+ # workitem.fields.delete_if { |k, v| k.match(/^private_/) }
162
+ # super(workitem)
163
+ # end
164
+ # end
165
+ #
166
+ class Participant
167
+ include Ruote::LocalParticipant
168
+
169
+ # Initializing the participant, right before calling #on_workitem or
170
+ # another on_ method.
171
+ #
172
+ def initialize(options)
173
+
174
+ @options = options
175
+
176
+ @conf = (@options['conf'] || 'params, fields, options').split(/\s*,\s*/)
177
+ @conf = %w[ params fields options ] if @conf.include?('all')
178
+
179
+ @field_prefix = @options['field_prefix'] || ''
180
+ end
181
+
182
+ # Workitem consumption code.
183
+ #
184
+ def on_workitem
185
+
186
+ instantiate_exchange.publish(
187
+ message,
188
+ :routing_key => routing_key,
189
+ :persistent => persistent,
190
+ :correlation_id => correlation_id)
191
+
192
+ reply if forget
193
+ end
194
+
195
+ def on_cancel
196
+
197
+ return if opt('discard_cancel')
198
+
199
+ instantiate_exchange.publish(
200
+ encode_cancelitem,
201
+ :routing_key => routing_key,
202
+ :persistent => persistent,
203
+ :correlation_id => correlation_id)
204
+ end
205
+
206
+ # No need for a dedicated thread when dispatching messages. Respond
207
+ # true.
208
+ #
209
+ def do_not_thread; true; end
210
+
211
+ # Returns the exchange coordinates (a triple [ type, name, options ]).
212
+ # Defaults to the direct exchange.
213
+ #
214
+ # Available as a method so it can be overriden (the return value could
215
+ # depend on the @workitem or other factors).
216
+ #
217
+ def exchange
218
+
219
+ opt('exchange') || [ 'direct', '', {} ]
220
+ #
221
+ # defaults to the "default exchange"...
222
+ end
223
+
224
+ # Returns the message to publish.
225
+ #
226
+ # Available as a method so it can be overriden (the return value could
227
+ # depend on the @workitem or other factors).
228
+ #
229
+ def message; opt('message') || encode_workitem; end
230
+
231
+ # Returns the routing key for the message to publish.
232
+ #
233
+ # Available as a method so it can be overriden (the return value could
234
+ # depend on the @workitem or other factors).
235
+ #
236
+ def routing_key; opt('routing_key'); end
237
+
238
+ # Returns whether the publish should be persistent or not.
239
+ #
240
+ # Available as a method so it can be overriden (the return value could
241
+ # depend on the @workitem or other factors).
242
+ #
243
+ def persistent; opt('persistent'); end
244
+
245
+ # Returns the correlation_id for the message publication. Returns ''
246
+ # by default.
247
+ #
248
+ # Available as a method so it can be overriden (the return value could
249
+ # depend on the @workitem or other factors).
250
+ #
251
+ def correlation_id
252
+
253
+ opt('correlation_id') || ''
254
+ end
255
+
256
+ # Returns something true-ish if the participant should not reply to the
257
+ # engine once the publish operation is done.
258
+ #
259
+ # Available as a method so it can be overriden (the return value could
260
+ # depend on the @workitem or other factors).
261
+ #
262
+ def forget; opt('forget'); end
263
+
264
+ protected
265
+
266
+ # How a workitem is turned into an AMQP message payload (string).
267
+ #
268
+ # Feel free to override this method to accommodate your needs.
269
+ #
270
+ def encode_workitem
271
+
272
+ workitem.as_json
273
+ end
274
+
275
+ # How a "cancelitem" is turned into an AMQP message payload (string).
276
+ #
277
+ # Feel free to override this method to accommodate your needs.
278
+ #
279
+ def encode_cancelitem
280
+
281
+ Rufus::Json.encode(
282
+ 'cancel' => true, 'fei' => @fei.h, 'flavour' => @flavour)
283
+ end
284
+
285
+ # Default AMQP.connect method.
286
+ #
287
+ # Feel free to override this method to accommodate your needs (See
288
+ # the README for an example).
289
+ #
290
+ def amqp_connect
291
+
292
+ ocon = opt('connection')
293
+
294
+ if Ruote::Amqp.session && ( ! ocon)
295
+ Ruote::Amqp.session
296
+ else
297
+ AMQP.connect(Ruote.keys_to_sym(ocon || {}))
298
+ end
299
+ end
300
+
301
+ # Given connection options passed at registration time (when the
302
+ # participant is registered in ruote) or from the process definition,
303
+ # returns an AMQP::Channel instance.
304
+ #
305
+ def channel
306
+
307
+ Thread.current['_ruote_amqp_channel'] ||= AMQP::Channel.new(amqp_connect)
308
+ end
309
+
310
+ # Given exchange options passed at registrations time or from the process
311
+ # definition, returns an AMQP::Exchange instance.
312
+ #
313
+ def instantiate_exchange
314
+
315
+ Thread.current['_ruote_amqp_exchange'] ||= begin
316
+
317
+ exc = exchange
318
+ type, name, options = exc
319
+
320
+ raise ArgumentError.new(
321
+ "couldn't determine exchange from #{exc.inspect}"
322
+ ) unless name
323
+
324
+ exchange_opts = (options || {}).each_with_object({}) { |(k, v), h|
325
+ h[k.to_sym] = v
326
+ }
327
+
328
+ AMQP::Exchange.new(channel, type.to_sym, name, exchange_opts)
329
+ end
330
+ end
331
+
332
+ # The mechanism for looking up options like 'connection', 'exchange',
333
+ # 'routing_key' in either the participant options, the process
334
+ # definition or the workitem fields...
335
+ #
336
+ def opt(key)
337
+
338
+ @conf.each do |type|
339
+
340
+ container = (type == 'options' ? @options : workitem.send(type))
341
+ k = type == 'fields' ? "#{@field_prefix}#{key}" : key
342
+
343
+ return container[k] if container.has_key?(k)
344
+ end
345
+
346
+ nil
347
+ end
348
+ end
349
+ end
350
+
@@ -0,0 +1,181 @@
1
+ #--
2
+ # Copyright (c) 2010-2012, Kenneth Kalmer, John Mettraux.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in
12
+ # all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, 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 IN
20
+ # THE SOFTWARE.
21
+ #++
22
+
23
+
24
+ module Ruote::Amqp
25
+
26
+ #
27
+ # A receiver is plugged between a ruote engine/storage and an AMQP queue.
28
+ # It will listen on the queue for messages, try to turn them into
29
+ # workitems and feed those workitem back to the engine/storage (in the usual
30
+ # use case, those workitems were initially emitted by the engine).
31
+ #
32
+ # == #decode_message(headers, payload)
33
+ #
34
+ # By default, the receiver expects the incoming workitem to be serialized
35
+ # entirely in the payload of the AMQP message. One can change this
36
+ # behaviour by overriding the #decode_workitem method (usually when
37
+ # subclassing)
38
+ #
39
+ # class MyYamlReceiver < Ruote::Amqp::Receiver
40
+ # def decode_message(headers, payload)
41
+ # YAML.load(payload)
42
+ # end
43
+ # end
44
+ #
45
+ # #decode_message is supposed to return a Ruby hash (describing either a
46
+ # workitem or a launchitem), the difference is explained below.
47
+ #
48
+ # === workitems and launchitems
49
+ #
50
+ # The standard use case is to accept workitems coming back (they probably
51
+ # left the engine via Ruote::Amqp::Participant). But it's also OK
52
+ # to accept "launchitems", hashes with at least one 'process_definition'
53
+ # (or 'definition') entry.
54
+ #
55
+ # Upon receiving a launchitem, the receiver will launch a new process
56
+ # instances.
57
+ #
58
+ # Launchitems may have two more optional entries, 'workitems_fields' (or
59
+ # 'fields') and 'process_variables' (or 'variables').
60
+ #
61
+ # 'workitem_fields' must contain a hash of initial workitem fields (they will
62
+ # populate the initial workitem.
63
+ #
64
+ # 'process_variables' are a very advanced option. It's possible to set the
65
+ # initial variables in a workflow. Read the general ruote documentation to
66
+ # learn about the difference between fields and variables.
67
+ #
68
+ # The #decode_message is supposed to return a hash representing either a
69
+ # workitem, either a launchitem.
70
+ #
71
+ # == #handle_error(err)
72
+ #
73
+ # Out of the box, the receiver will print out to $stderr the details of
74
+ # errors it encounters when receiving, decoding and handing back the
75
+ # workitems (messages?) to the engine. This can be changed by overriding
76
+ # the #handle_error method:
77
+ #
78
+ # class MyReceiver < Ruote::Amqp::Receiver
79
+ # def handle_error(err)
80
+ # ThatLoggerService.log(err)
81
+ # end
82
+ # end
83
+ #
84
+ #
85
+ # == initialization options
86
+ #
87
+ # One can set the "launch_only" option to true when initializing the receiver
88
+ # to prevent it from handling anything but launchitems.
89
+ #
90
+ # receiver = Ruote::Amqp::Receiver.new(
91
+ # @dashboard, @amqp_queue, :launch_only => true)
92
+ #
93
+ # 'launch_only' (string) is valid too.
94
+ #
95
+ class Receiver < Ruote::Receiver
96
+
97
+ attr_reader :queue
98
+
99
+ def initialize(engine_or_storage, queue, options={})
100
+
101
+ super(engine_or_storage, Ruote.keys_to_s(options))
102
+
103
+ @queue = queue
104
+ @queue.subscribe(&method(:handle))
105
+ end
106
+
107
+ def shutdown
108
+
109
+ @queue.unsubscribe
110
+ end
111
+
112
+ protected
113
+
114
+ def handle(header, payload)
115
+
116
+ item = decode_message(header, payload)
117
+
118
+ if item['error'] && item['fei']
119
+ flunk(item)
120
+ elsif item['fields'] && item['fei']
121
+ receive(item)
122
+ elsif item['process_definition'] || item['definition']
123
+ launch(item)
124
+ else
125
+ raise ArgumentError.new("cannot receive or launch #{item.inspect}")
126
+ end
127
+
128
+ rescue => e
129
+ handle_error(e)
130
+ end
131
+
132
+ def decode_message(header, payload)
133
+
134
+ Rufus::Json.decode(payload)
135
+ end
136
+
137
+ def handle_error(err)
138
+
139
+ $stderr.puts '**err**'
140
+ $stderr.puts err.inspect
141
+ $stderr.puts err.backtrace
142
+ end
143
+
144
+ def flunk(h)
145
+
146
+ return if @options['launch_only']
147
+
148
+ err = h.delete('error')
149
+
150
+ args = case err
151
+ when String then [ RemoteError, err ]
152
+ when Hash then [ Ruote.constantize(err['class']), err['message'] ]
153
+ else [ RemoteError, err.inspect ]
154
+ end
155
+
156
+ super(h, *args)
157
+ end
158
+
159
+ def receive(h)
160
+
161
+ return if @options['launch_only']
162
+
163
+ super(h)
164
+ end
165
+
166
+ def launch(h)
167
+
168
+ super(
169
+ h['process_definition'] || h['definition'],
170
+ h['workitem_fields'] || h['fields'] || {},
171
+ h['process_variables'] || h['variables'] || {})
172
+ end
173
+ end
174
+
175
+ #
176
+ # Used to wrap errors that come as string (well, errors that don't come
177
+ # with a class name). Could be thought of as "anonymous remote error".
178
+ #
179
+ class RemoteError < StandardError; end
180
+ end
181
+
@@ -0,0 +1,28 @@
1
+ #--
2
+ # Copyright (c) 2010-2012, Kenneth Kalmer, John Mettraux.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in
12
+ # all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, 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 IN
20
+ # THE SOFTWARE.
21
+ #++
22
+
23
+
24
+ module Ruote::Amqp
25
+
26
+ VERSION = '2.3.0'
27
+ end
28
+
data/lib/ruote/amqp.rb ADDED
@@ -0,0 +1,68 @@
1
+ #--
2
+ # Copyright (c) 2010-2012, Kenneth Kalmer, John Mettraux.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in
12
+ # all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, 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 IN
20
+ # THE SOFTWARE.
21
+ #++
22
+
23
+ require 'amqp'
24
+ require 'ruote'
25
+
26
+ require 'ruote/amqp/participant'
27
+ require 'ruote/amqp/alert_participant'
28
+ require 'ruote/amqp/receiver'
29
+
30
+
31
+ module Ruote::Amqp
32
+
33
+ # Returns the AMQP::Session shared by the ruote participants (and potentially
34
+ # the receivers too).
35
+ #
36
+ # Returns nil if none is set (the participant will mostly create a connection
37
+ # on their own).
38
+ #
39
+ # See http://rubydoc.info/github/ruby-amqp/amqp/master/AMQP/Session
40
+ #
41
+ def self.session
42
+
43
+ @session
44
+ end
45
+
46
+ # Sets the AMQP::Session shared by the ruote participants (and potentially
47
+ # the receivers too).
48
+ #
49
+ # Ruote::Amqp.session = AMQP.connect(:auto_recovery => true) do |con|
50
+ # con.on_recovery do |con|
51
+ # puts "Recovered..."
52
+ # end
53
+ # connection.on_tcp_connection_loss do |con, settings|
54
+ # puts "Reconnecting... please wait"
55
+ # conn.reconnect(false, 20)
56
+ # end
57
+ # end
58
+ #
59
+ # (Thanks Jim Li - https://github.com/marsbomber/ruote-amqp/commit/0f36a41f)
60
+ #
61
+ # See http://rubydoc.info/github/ruby-amqp/amqp/master/AMQP/Session
62
+ #
63
+ def self.session=(s)
64
+
65
+ @session = s
66
+ end
67
+ end
68
+
data/lib/ruote-amqp.rb CHANGED
@@ -1,80 +1,3 @@
1
1
 
2
- require 'mq'
3
-
4
- require 'ruote-amqp/version'
5
-
6
-
7
- #
8
- # AMQP participant and listener pair for ruote.
9
- #
10
- # == Documentation
11
- #
12
- # See #RuoteAMQP::Listener and #RuoteAMQP::Participant for detailed
13
- # documentation on using each of them.
14
- #
15
- # == AMQP Notes
16
- #
17
- # RuoteAMQP uses durable queues and persistent messages by default, to ensure
18
- # no messages get lost along the way and that running expressions doesn't have
19
- # to be restarted in order for messages to be resent.
20
- #
21
- module RuoteAMQP
22
-
23
- autoload 'ParticipantProxy', 'ruote-amqp/participant'
24
-
25
- autoload 'Receiver', 'ruote-amqp/receiver'
26
- autoload 'WorkitemListener', 'ruote-amqp/workitem_listener'
27
- autoload 'LaunchitemListener', 'ruote-amqp/launchitem_listener'
28
-
29
- class << self
30
-
31
- attr_writer :use_persistent_messages
32
-
33
- # Whether or not to use persistent messages (true by default)
34
- def use_persistent_messages?
35
- @use_persistent_messages = true if @use_persistent_messages.nil?
36
- @use_persistent_messages
37
- end
38
-
39
- # Ensure the AMQP connection is started
40
- def start!
41
- return if started?
42
-
43
- mutex = Mutex.new
44
- cv = ConditionVariable.new
45
-
46
- Thread.main[:ruote_amqp_connection] = Thread.new do
47
- Thread.abort_on_exception = true
48
- AMQP.start {
49
- started!
50
- cv.signal
51
- }
52
- end
53
-
54
- mutex.synchronize { cv.wait(mutex) }
55
-
56
- MQ.prefetch(1)
57
-
58
- yield if block_given?
59
- end
60
-
61
- # Check whether the AMQP connection is started
62
- def started?
63
- Thread.main[:ruote_amqp_started] == true
64
- end
65
-
66
- def started! #:nodoc:
67
- Thread.main[:ruote_amqp_started] = true
68
- end
69
-
70
- # Close down the AMQP connections
71
- def stop!
72
- return unless started?
73
-
74
- AMQP.stop
75
- Thread.main[:ruote_amqp_connection].join
76
- Thread.main[:ruote_amqp_started] = false
77
- end
78
- end
79
- end
2
+ require 'ruote/amqp'
80
3