ruote-amqp 2.1.5 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/{History.txt → CHANGELOG.txt} +18 -4
- data/CREDITS.txt +29 -0
- data/README.rdoc +6 -6
- data/Rakefile +101 -50
- data/TODO.txt +7 -3
- data/lib/ruote-amqp.rb +8 -3
- data/lib/ruote-amqp/launchitem_listener.rb +10 -76
- data/lib/ruote-amqp/participant.rb +154 -99
- data/lib/ruote-amqp/receiver.rb +147 -0
- data/lib/ruote-amqp/version.rb +6 -0
- data/lib/ruote-amqp/workitem_listener.rb +5 -77
- data/ruote-amqp.gemspec +24 -75
- data/spec/launchitem_listener_spec.rb +49 -11
- data/spec/participant_spec.rb +71 -50
- data/spec/receiver_spec.rb +126 -0
- data/spec/ruote_amqp_spec.rb +9 -3
- data/spec/spec_helper.rb +18 -18
- data/spec/support/ruote_helpers.rb +29 -0
- data/{lib/spec → spec/support}/ruote_matchers.rb +14 -9
- data/spec/workitem_listener_spec.rb +30 -15
- metadata +90 -64
- data/.gitignore +0 -6
- data/Manifest.txt +0 -24
- data/lib/spec/ruote.rb +0 -5
- data/lib/spec/ruote_example_group.rb +0 -8
- data/lib/spec/ruote_helpers.rb +0 -27
- data/script/console +0 -10
- data/script/destroy +0 -14
- data/script/generate +0 -14
- data/spec/spec.opts +0 -1
@@ -1,22 +1,36 @@
|
|
1
|
-
|
1
|
+
|
2
|
+
= ruote-amqp
|
3
|
+
|
4
|
+
|
5
|
+
== ruote-amqp - 2.2.0 released 2011/03/01
|
6
|
+
|
7
|
+
- receiver : exposing #decode_workitem for overwriting
|
8
|
+
- options[:queue] patch by Mario Camou
|
9
|
+
- :durable path by belucid
|
10
|
+
- 1 MQ per dispatch thread 'leak' fix. Thanks weifeng365
|
11
|
+
- RuoteAMQP::Participant --> RuoteAMQP::ParticipantProxy
|
12
|
+
|
13
|
+
|
14
|
+
== 2.0.0 not released
|
2
15
|
|
3
16
|
* Compatible with ruote 2.0
|
4
17
|
* Thanks to John Mettraux (http://github.com/jmettraux/ruote-amqp)
|
5
18
|
* Thanks to Jason & Jordan (http://github.com/asm/ruote-amqp)
|
6
19
|
* Thanks to Charles Magid (http://github.com/ChasManRors/ruote-amqp)
|
7
20
|
|
8
|
-
|
21
|
+
== 0.9.21.1 2009-08-03
|
9
22
|
|
10
23
|
* Switch to using persistent AMQP messages by default
|
11
24
|
|
12
|
-
|
25
|
+
== 0.9.21 2009-07-13
|
13
26
|
|
14
27
|
* Depend on ruote-0.9.21 for flexible JSON backends
|
15
28
|
* Support for default queues
|
16
29
|
* Support for mapping participant names to queue names
|
17
30
|
* Plenty of RDOC fixed
|
18
31
|
|
19
|
-
|
32
|
+
== 0.9.20 2009-07-13
|
20
33
|
|
21
34
|
* 1 major enhancement:
|
22
35
|
* Initial release
|
36
|
+
|
data/CREDITS.txt
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
|
2
|
+
= CREDITS
|
3
|
+
|
4
|
+
(probably incomplete, don't hesitate to tell us if a mention is missing)
|
5
|
+
|
6
|
+
|
7
|
+
== AUTHORS
|
8
|
+
|
9
|
+
* Kenneth Kalmer - http://www.opensourcery.co.za/
|
10
|
+
* John Mettraux - https://github.com/jmettraux
|
11
|
+
|
12
|
+
|
13
|
+
== CONTRIBUTORS
|
14
|
+
|
15
|
+
* Mario Camou
|
16
|
+
* Sean Johnson - https://github.com/belucid
|
17
|
+
* Victor Liu - https://github.com/pennymax
|
18
|
+
* weifeng - https://github.com/weifeng365
|
19
|
+
* David Greaves - https://github.com/lbt
|
20
|
+
* Hartog C. de Mik - https://github.com/coffeeaddict
|
21
|
+
* Torsten Schoenebaum - https://github.com/tosch
|
22
|
+
* Jordan Ritter - https://github.com/jpr5
|
23
|
+
* Charles Magid - https://github.com/ChasManRors
|
24
|
+
* Marc Mauger - https://github.com/simianarmy
|
25
|
+
* Jason - https://github.com/asm
|
26
|
+
|
27
|
+
|
28
|
+
== FEEDBACK
|
29
|
+
|
data/README.rdoc
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
|
1
2
|
= ruote-amqp
|
2
3
|
|
3
4
|
* http://github.com/kennethkalmer/ruote-amqp
|
@@ -16,7 +17,7 @@ http://ruote.rubyforge.org/part_implementations.html
|
|
16
17
|
== FEATURES/PROBLEMS:
|
17
18
|
|
18
19
|
* Flexible participant for sending workitems
|
19
|
-
* Flexible
|
20
|
+
* Flexible receiver for receiving replies
|
20
21
|
* Flexible launch item listener for launching processes over AMQP
|
21
22
|
* Fully evented (thanks to the amqp gem)
|
22
23
|
|
@@ -26,9 +27,8 @@ Please review the rdoc in RuoteAMQP::Participant and Ruote::AMQP::Listener
|
|
26
27
|
|
27
28
|
== REQUIREMENTS:
|
28
29
|
|
29
|
-
* ruote[http://ruote.rubyforge.org] 2.
|
30
|
-
* amqp[http://github.com/tmm1/amqp] 0.6.
|
31
|
-
* rufus-json[http://github.com/jmettraux/rufus-json] 0.1.0 or later
|
30
|
+
* ruote[http://ruote.rubyforge.org] 2.2.0 or later
|
31
|
+
* amqp[http://github.com/tmm1/amqp] 0.6.7 or later
|
32
32
|
* rabbitmq[http://www.rabbitmq.com/] 1.6.0 or later
|
33
33
|
|
34
34
|
== INSTALL:
|
@@ -40,7 +40,7 @@ Please be sure to have read the requirements section above
|
|
40
40
|
== TESTS:
|
41
41
|
|
42
42
|
To run the tests you need the following requirements met, or the testing environment
|
43
|
-
will fail horribly.
|
43
|
+
will fail horribly (or simply get stuck without output).
|
44
44
|
|
45
45
|
=== RabbitMQ vhost
|
46
46
|
|
@@ -69,7 +69,7 @@ DaemonKit doesn't currently support ruote 2.1, support is forthcoming.
|
|
69
69
|
|
70
70
|
(The MIT License)
|
71
71
|
|
72
|
-
Copyright (c) 2010 Kenneth Kalmer
|
72
|
+
Copyright (c) 2010-2011 Kenneth Kalmer
|
73
73
|
|
74
74
|
Permission is hereby granted, free of charge, to any person obtaining
|
75
75
|
a copy of this software and associated documentation files (the
|
data/Rakefile
CHANGED
@@ -1,50 +1,101 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
require '
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
task :
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
1
|
+
|
2
|
+
$:.unshift('.') # 1.9.2
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
require 'rubygems/user_interaction' if Gem::RubyGemsVersion == '1.5.0'
|
6
|
+
|
7
|
+
require 'rake'
|
8
|
+
require 'rake/clean'
|
9
|
+
require 'rake/rdoctask'
|
10
|
+
|
11
|
+
|
12
|
+
#
|
13
|
+
# clean
|
14
|
+
|
15
|
+
CLEAN.include('pkg', 'rdoc')
|
16
|
+
|
17
|
+
|
18
|
+
#
|
19
|
+
# test / spec
|
20
|
+
|
21
|
+
task :spec do
|
22
|
+
|
23
|
+
sh 'rspec spec/'
|
24
|
+
end
|
25
|
+
|
26
|
+
task :test => [ :spec ]
|
27
|
+
task :default => [ :spec ]
|
28
|
+
|
29
|
+
|
30
|
+
#
|
31
|
+
# gem
|
32
|
+
|
33
|
+
GEMSPEC_FILE = Dir['*.gemspec'].first
|
34
|
+
GEMSPEC = eval(File.read(GEMSPEC_FILE))
|
35
|
+
GEMSPEC.validate
|
36
|
+
|
37
|
+
|
38
|
+
desc %{
|
39
|
+
builds the gem and places it in pkg/
|
40
|
+
}
|
41
|
+
task :build do
|
42
|
+
|
43
|
+
sh "gem build #{GEMSPEC_FILE}"
|
44
|
+
sh "mkdir pkg" rescue nil
|
45
|
+
sh "mv #{GEMSPEC.name}-#{GEMSPEC.version}.gem pkg/"
|
46
|
+
end
|
47
|
+
|
48
|
+
desc %{
|
49
|
+
builds the gem and pushes it to rubygems.org
|
50
|
+
}
|
51
|
+
task :push => :build do
|
52
|
+
|
53
|
+
sh "gem push pkg/#{GEMSPEC.name}-#{GEMSPEC.version}.gem"
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
#
|
58
|
+
# rabbitmq preparation
|
59
|
+
|
60
|
+
desc %{
|
61
|
+
prepare RabbitMQ (vhost, user, perms)
|
62
|
+
}
|
63
|
+
task :prepare do
|
64
|
+
|
65
|
+
sh "rabbitmqctl add_vhost ruote-test"
|
66
|
+
sh "rabbitmqctl add_user ruote ruote"
|
67
|
+
sh "rabbitmqctl set_permissions -p ruote-test ruote '.*' '.*' '.*'"
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
#
|
72
|
+
# rdoc
|
73
|
+
#
|
74
|
+
# make sure to have rdoc 2.5.x to run that
|
75
|
+
|
76
|
+
Rake::RDocTask.new do |rd|
|
77
|
+
|
78
|
+
rd.main = 'README.rdoc'
|
79
|
+
rd.rdoc_dir = 'rdoc'
|
80
|
+
|
81
|
+
rd.rdoc_files.include(
|
82
|
+
'README.rdoc', 'CHANGELOG.txt', 'CREDITS.txt', 'lib/**/*.rb')
|
83
|
+
|
84
|
+
rd.title = "#{GEMSPEC.name} #{GEMSPEC.version}"
|
85
|
+
end
|
86
|
+
|
87
|
+
|
88
|
+
#
|
89
|
+
# upload_rdoc
|
90
|
+
|
91
|
+
desc %{
|
92
|
+
upload the rdoc to rubyforge
|
93
|
+
}
|
94
|
+
task :upload_rdoc => [ :clean, :rdoc ] do
|
95
|
+
|
96
|
+
account = 'jmettraux@rubyforge.org'
|
97
|
+
webdir = '/var/www/gforge-projects/ruote'
|
98
|
+
|
99
|
+
sh "rsync -azv -e ssh rdoc/#{GEMSPEC.name}_rdoc #{account}:#{webdir}/"
|
100
|
+
end
|
101
|
+
|
data/TODO.txt
CHANGED
@@ -1,4 +1,8 @@
|
|
1
|
-
ruote-amqp TODO
|
2
|
-
===============
|
3
1
|
|
4
|
-
[
|
2
|
+
[o] Launch processes over AMQP (LaunchitemListener)
|
3
|
+
[o] Receiver
|
4
|
+
[o] ParticipantProxy ?
|
5
|
+
|
6
|
+
[ ] have a class method ParticipantProxy.stop_all ?
|
7
|
+
[ ] use Ruote::Workitem #as_json and #from_json(s)
|
8
|
+
|
data/lib/ruote-amqp.rb
CHANGED
@@ -1,5 +1,10 @@
|
|
1
|
+
|
1
2
|
require 'mq'
|
2
3
|
|
4
|
+
require 'ruote-amqp/version'
|
5
|
+
|
6
|
+
|
7
|
+
#
|
3
8
|
# AMQP participant and listener pair for ruote.
|
4
9
|
#
|
5
10
|
# == Documentation
|
@@ -15,9 +20,9 @@ require 'mq'
|
|
15
20
|
#
|
16
21
|
module RuoteAMQP
|
17
22
|
|
18
|
-
|
23
|
+
autoload 'ParticipantProxy', 'ruote-amqp/participant'
|
19
24
|
|
20
|
-
autoload '
|
25
|
+
autoload 'Receiver', 'ruote-amqp/receiver'
|
21
26
|
autoload 'WorkitemListener', 'ruote-amqp/workitem_listener'
|
22
27
|
autoload 'LaunchitemListener', 'ruote-amqp/launchitem_listener'
|
23
28
|
|
@@ -70,6 +75,6 @@ module RuoteAMQP
|
|
70
75
|
Thread.main[:ruote_amqp_connection].join
|
71
76
|
Thread.main[:ruote_amqp_started] = false
|
72
77
|
end
|
73
|
-
|
74
78
|
end
|
75
79
|
end
|
80
|
+
|
@@ -1,88 +1,22 @@
|
|
1
|
+
|
1
2
|
module RuoteAMQP
|
2
3
|
|
3
|
-
# = AMQP Launchitem Listener
|
4
|
-
#
|
5
|
-
# Used on its own, the RuoteAMQP::LaunchitemListener provides the engine with
|
6
|
-
# a way to launch process definitions over an AMQP direct exchange.
|
7
|
-
#
|
8
|
-
# == Message Format
|
9
|
-
#
|
10
|
-
# The LaunchitemListener expects JSON formatted messages that look like this:
|
11
|
-
#
|
12
|
-
# {
|
13
|
-
# "definition" : "process definition",
|
14
|
-
# "fields" : { "key" : "value" },
|
15
|
-
# "variables" : { "key" : "value" }
|
16
|
-
# }
|
17
|
-
#
|
18
|
-
# The definition key is a complete string representation of a business process.
|
19
|
-
#
|
20
|
-
# == Configuration
|
21
4
|
#
|
22
|
-
#
|
23
|
-
# +AMQP.settings+ hash, as provided by the AMQP gem. No defaults are set by
|
24
|
-
# the listener. The only +option+ parsed by the initializer is the +queue+
|
25
|
-
# key (in the optional hash). If no +queue+ key is provided, the listener
|
26
|
-
# will subscribe to the +ruote_launchitems+ direct exchange for launchitems.
|
5
|
+
# Got replaced by RuoteAMQP::Receiver
|
27
6
|
#
|
28
|
-
#
|
7
|
+
# This class is kept for backward compatibility.
|
29
8
|
#
|
30
|
-
|
31
|
-
#
|
32
|
-
# Register the engine with the listener:
|
33
|
-
#
|
34
|
-
# RuoteAMQP::LaunchitemListener.new( engine_instance )
|
35
|
-
#
|
36
|
-
# The workitem listener leverages the asynchronous nature of the amqp gem,
|
37
|
-
# so no timers are setup when initialized.
|
38
|
-
class LaunchitemListener < Ruote::Receiver
|
39
|
-
|
40
|
-
class << self
|
41
|
-
|
42
|
-
# Listening queue - set this before initialization
|
43
|
-
attr_writer :queue
|
44
|
-
|
45
|
-
def queue
|
46
|
-
@queue ||= 'ruote_launchitems'
|
47
|
-
end
|
48
|
-
|
49
|
-
end
|
9
|
+
class LaunchitemListener < ::RuoteAMQP::Receiver
|
50
10
|
|
51
11
|
# Start a new LaunchItem listener
|
52
12
|
#
|
53
|
-
# @param [ Ruote::Engine ]
|
54
|
-
# @param
|
55
|
-
|
56
|
-
|
57
|
-
self.class.queue = queue if queue
|
58
|
-
|
59
|
-
RuoteAMQP.start!
|
60
|
-
|
61
|
-
MQ.queue( self.class.queue, :durable => true ).subscribe do |message|
|
62
|
-
if AMQP.closing?
|
63
|
-
# Do nothing, we're going down
|
64
|
-
else
|
65
|
-
launchitem = decode_launchitem( message )
|
66
|
-
engine.launch( *launchitem )
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
def stop
|
72
|
-
RuoteAMQP.stop!
|
73
|
-
end
|
74
|
-
|
75
|
-
private
|
76
|
-
|
77
|
-
# Complicated guesswork that needs to happen here to detect the format
|
78
|
-
def decode_launchitem( msg )
|
79
|
-
hash = Rufus::Json.decode( msg )
|
80
|
-
opts = {}
|
81
|
-
definition = hash.delete('definition')
|
82
|
-
fields = hash.delete('fields') || {}
|
83
|
-
variables = hash.delete('variables') || {}
|
13
|
+
# @param [ Ruote::Engine, Ruote::Storage ] A configured ruote engine or storage instance
|
14
|
+
# @param opts :queue / :unsubscribe
|
15
|
+
#
|
16
|
+
def initialize(engine_or_storage, opts={})
|
84
17
|
|
85
|
-
|
18
|
+
super(engine_or_storage, opts.merge(:launchitems => :only))
|
86
19
|
end
|
87
20
|
end
|
88
21
|
end
|
22
|
+
|
@@ -1,19 +1,37 @@
|
|
1
|
+
|
1
2
|
require 'ruote/part/local_participant'
|
3
|
+
require 'ruote-amqp'
|
4
|
+
|
2
5
|
|
3
6
|
module RuoteAMQP
|
4
7
|
|
8
|
+
#
|
5
9
|
# = AMQP Participants
|
6
10
|
#
|
7
|
-
# The RuoteAMQP::
|
11
|
+
# The RuoteAMQP::ParticipantProxy allows you to send workitems (serialized as
|
8
12
|
# JSON) or messages to any AMQP queues right from the process
|
9
|
-
# definition. When combined with the RuoteAMQP::
|
13
|
+
# definition. When combined with the RuoteAMQP::Receiver you can easily
|
10
14
|
# leverage an extremely powerful local/remote participant
|
11
15
|
# combinations.
|
12
16
|
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
+
# For local/remote participants The local part of the
|
18
|
+
# RuoteAMQP::ParticipantProxy relies on the presence of a
|
19
|
+
# RuoteAMQP::Receiver. Workitems are sent to the remote participant
|
20
|
+
# and the local part does not normally reply to the engine. Instead
|
21
|
+
# the engine will continue when a reply is received on the
|
22
|
+
# 'ruote_workitems' queue (see RuoteAMQP::Receiver).
|
23
|
+
#
|
24
|
+
# Of course, the standard :forget => true format can be used even
|
25
|
+
# with remote particpants and :forget can even be set as a default in
|
26
|
+
# the options.
|
27
|
+
#
|
28
|
+
# A simple way to create a remote participant to act upon workitems
|
29
|
+
# is to use the daemon-kit ruote responder.
|
30
|
+
#
|
31
|
+
# Simple AMQP messages are treated as 'fire and forget' and the flow
|
32
|
+
# will continue when the local participant has queued the message
|
33
|
+
# for sending. (As there is no meaningful way to receive a workitem
|
34
|
+
# in reply).
|
17
35
|
#
|
18
36
|
# == Configuration
|
19
37
|
#
|
@@ -21,86 +39,59 @@ module RuoteAMQP
|
|
21
39
|
# values of the +AMQP.settings+ hash, as provided by the AMQP
|
22
40
|
# gem. No AMQP defaults are set by the participant.
|
23
41
|
#
|
24
|
-
# The participant requires version 0.6.1 or later of the amqp gem.
|
25
|
-
#
|
26
42
|
# == Usage
|
27
43
|
#
|
28
|
-
#
|
29
|
-
# directly to a specific queue, and have the engine wait for
|
30
|
-
# replies on another queue (see AMQPListener).
|
31
|
-
#
|
32
|
-
# Setting up the participant
|
44
|
+
# Define the queue used by an AMQP participant :
|
33
45
|
#
|
34
46
|
# engine.register_participant(
|
35
|
-
# :
|
47
|
+
# :delete_user, RuoteAMQP::ParticipantProxy, 'queue' => 'user_manager')
|
36
48
|
#
|
37
|
-
#
|
49
|
+
# Sending a workitem to the remote participant defined above:
|
38
50
|
#
|
39
|
-
#
|
40
|
-
# :amqp, RuoteAMQP::Participant.new(:reply_by_default => true ) )
|
41
|
-
#
|
42
|
-
# Sending a message example
|
43
|
-
#
|
44
|
-
# class AmqpMessageExample0 < OpenWFE::ProcessDefinition
|
51
|
+
# Ruote.process_definition do
|
45
52
|
# sequence do
|
46
|
-
#
|
53
|
+
# delete_user
|
47
54
|
# end
|
48
55
|
# end
|
49
56
|
#
|
50
|
-
#
|
57
|
+
# Let the local participant reply to the engine without involving
|
58
|
+
# the receiver
|
51
59
|
#
|
52
|
-
#
|
60
|
+
# Ruote.process_definition do
|
53
61
|
# sequence do
|
54
|
-
#
|
62
|
+
# delete_user :forget => true
|
55
63
|
# end
|
56
64
|
# end
|
57
65
|
#
|
58
|
-
#
|
66
|
+
# Setting up the participant in a slightly more 'raw' way:
|
67
|
+
#
|
68
|
+
# engine.register_participant(
|
69
|
+
# :amqp, RuoteAMQP::ParticipantProxy )
|
70
|
+
#
|
71
|
+
# Sending a workitem to a specific queue:
|
59
72
|
#
|
60
|
-
#
|
73
|
+
# Ruote.process_definition do
|
61
74
|
# sequence do
|
62
|
-
# amqp :queue => 'test',
|
75
|
+
# amqp :queue => 'test', 'command' => '/run/regression_test'
|
63
76
|
# end
|
64
77
|
# end
|
65
78
|
#
|
66
|
-
#
|
67
|
-
#
|
68
|
-
# Keeping things DRY with participant name to queue maps:
|
69
|
-
#
|
70
|
-
# amqp = RuoteAMQP::Participant.new( :default_queue => 'test' )
|
71
|
-
# amqp.map_participant 'george', 'whitehouse'
|
72
|
-
# amqp.map_participant 'barak', 'whitehouse'
|
73
|
-
# amqp.map_participant 'greenspan', 'treasury'
|
79
|
+
# Setup a 'fire and forget' participant that always replies to the
|
80
|
+
# engine:
|
74
81
|
#
|
75
|
-
# engine.register_participant(
|
76
|
-
#
|
77
|
-
# engine.register_participant( :greespan, amqp )
|
78
|
-
# engine.register_participant( :amqp, amqp )
|
79
|
-
#
|
80
|
-
# class DryAmqProcess0 < OpenWFE::ProcessDefinition
|
81
|
-
# cursor :break_if => "${f:economy_recovered}" do
|
82
|
-
# # Workitem sent to 'whitehouse' queue
|
83
|
-
# george :activity => 'Tank economy'
|
84
|
-
#
|
85
|
-
# # Workitem sent to 'treasury' queue
|
86
|
-
# greenspan :activity => 'Resign'
|
82
|
+
# engine.register_participant(
|
83
|
+
# :jfdi, RuoteAMQP::ParticipantProxy, 'forget' => true )
|
87
84
|
#
|
88
|
-
#
|
89
|
-
#
|
85
|
+
# Sending a message example to a specific queue (both steps are
|
86
|
+
# equivalent):
|
90
87
|
#
|
91
|
-
#
|
92
|
-
#
|
88
|
+
# Ruote.process_definition do
|
89
|
+
# sequence do
|
90
|
+
# amqp :queue => 'test', :message => 'foo'
|
91
|
+
# amqp :queue => 'test', :message => 'foo', :forget => true
|
93
92
|
# end
|
94
93
|
# end
|
95
94
|
#
|
96
|
-
# == Workitem modifications
|
97
|
-
#
|
98
|
-
# To ease replies, and additional workitem attribute is set:
|
99
|
-
#
|
100
|
-
# 'reply_queue'
|
101
|
-
#
|
102
|
-
# +reply_queue+ has the name of the queue where the RuoteAMQP::Listener
|
103
|
-
# expects replies from remote participants
|
104
95
|
#
|
105
96
|
# == AMQP notes
|
106
97
|
#
|
@@ -112,27 +103,33 @@ module RuoteAMQP
|
|
112
103
|
# participant, and messages are marked as persistent by default (see
|
113
104
|
# #RuoteAMQP)
|
114
105
|
#
|
115
|
-
class
|
106
|
+
class ParticipantProxy
|
116
107
|
|
117
108
|
include Ruote::LocalParticipant
|
118
109
|
|
119
|
-
#
|
110
|
+
# The following parameters are used in the process definition.
|
120
111
|
#
|
121
|
-
#
|
122
|
-
#
|
123
|
-
|
112
|
+
# An options hash with the same keys to provide defaults is
|
113
|
+
# accepted at registration time (see above).
|
114
|
+
#
|
115
|
+
# * :queue => (string) The AMQP queue used by the remote participant.
|
116
|
+
# nil by default.
|
117
|
+
# * :forget => (bool) Whether the flow should block until the remote
|
118
|
+
# participant replies.
|
119
|
+
# false by default
|
120
|
+
#
|
121
|
+
def initialize(options)
|
122
|
+
|
124
123
|
RuoteAMQP.start!
|
125
124
|
|
126
125
|
@options = {
|
127
|
-
|
128
|
-
|
129
|
-
}.merge(
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
def map_participant( name, queue )
|
135
|
-
@participant_maps[ name ] = queue
|
126
|
+
'queue' => nil,
|
127
|
+
'forget' => false,
|
128
|
+
}.merge(options.inject({}) { |h, (k, v)|
|
129
|
+
h[k.to_s] = v; h
|
130
|
+
})
|
131
|
+
#
|
132
|
+
# the inject is here to make sure that all options have String keys
|
136
133
|
end
|
137
134
|
|
138
135
|
# Process the workitem at hand. By default the workitem will be
|
@@ -140,47 +137,105 @@ module RuoteAMQP
|
|
140
137
|
# workitem parameter. You can specify a +message+ workitem
|
141
138
|
# parameter to have that sent instead of the workitem.
|
142
139
|
#
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
140
|
+
def consume(workitem)
|
141
|
+
|
142
|
+
target_queue = determine_queue(workitem)
|
143
|
+
|
144
|
+
raise 'no queue specified (outbound delivery)' unless target_queue
|
145
|
+
|
146
|
+
q = MQ.queue(target_queue, :durable => true)
|
147
|
+
forget = determine_forget(workitem)
|
148
|
+
|
149
|
+
opts = {
|
150
|
+
:persistent => RuoteAMQP.use_persistent_messages?,
|
151
|
+
:content_type => 'application/json' }
|
152
|
+
|
153
|
+
if message = workitem.fields['message'] || workitem.params['message']
|
154
|
+
|
155
|
+
forget = true # sending a message implies 'forget' => true
|
156
|
+
|
157
|
+
q.publish(message, opts)
|
158
|
+
|
156
159
|
else
|
157
|
-
raise "no queue in workitem params!"
|
158
|
-
end
|
159
160
|
|
160
|
-
|
161
|
-
reply_to_engine( workitem )
|
161
|
+
q.publish(encode_workitem(workitem), opts)
|
162
162
|
end
|
163
|
+
|
164
|
+
reply_to_engine(workitem) if forget
|
163
165
|
end
|
164
166
|
|
167
|
+
# (Stops the underlying queue subscription)
|
168
|
+
#
|
165
169
|
def stop
|
170
|
+
|
166
171
|
RuoteAMQP.stop!
|
167
172
|
end
|
168
173
|
|
169
174
|
def cancel(fei, flavour)
|
175
|
+
#
|
176
|
+
# TODO : sending a cancel item is not a bad idea, especially if the
|
177
|
+
# job done over the amqp fence lasts...
|
178
|
+
#
|
179
|
+
end
|
180
|
+
|
181
|
+
# The current AMQP (0.6.7) has 1 queue per thread. If you let the default
|
182
|
+
# "one thread per participant consume call" kick in, you'll end up with
|
183
|
+
# 1 queue per consume call (and...)
|
184
|
+
#
|
185
|
+
# So, by returning true here, we force the queue to be always the same.
|
186
|
+
#
|
187
|
+
# Many thanks to https://github.com/weifeng365 for reporting this issue
|
188
|
+
# and suggesting the fix.
|
189
|
+
#
|
190
|
+
# TODO : should we have something to close queues when the engine / worker
|
191
|
+
# shuts down ?
|
192
|
+
# or is it already covered in the #stop ?
|
193
|
+
#
|
194
|
+
def do_not_thread
|
195
|
+
|
196
|
+
true
|
170
197
|
end
|
171
198
|
|
172
199
|
private
|
173
200
|
|
174
|
-
def
|
175
|
-
|
176
|
-
|
177
|
-
@options[
|
201
|
+
def determine_forget(workitem)
|
202
|
+
|
203
|
+
return workitem.params['forget'] if workitem.params.has_key?('forget')
|
204
|
+
return @options['forget'] if @options.has_key?('forget')
|
205
|
+
false
|
206
|
+
end
|
207
|
+
|
208
|
+
def determine_queue(workitem)
|
209
|
+
|
210
|
+
workitem.params['queue'] || @options['queue']
|
211
|
+
end
|
212
|
+
|
213
|
+
# Encodes the workitem as JSON. Makes sure to add to the field 'params'
|
214
|
+
# an entry named 'participant_options' which contains the options of
|
215
|
+
# this participant.
|
216
|
+
#
|
217
|
+
def encode_workitem(wi)
|
218
|
+
|
219
|
+
wi.params['participant_options'] = @options
|
220
|
+
|
221
|
+
Rufus::Json.encode(wi.to_h)
|
178
222
|
end
|
223
|
+
end
|
224
|
+
|
225
|
+
#
|
226
|
+
# Kept for backward compatibility.
|
227
|
+
#
|
228
|
+
# You should use RuoteAMQP::ParticipantProxy.
|
229
|
+
#
|
230
|
+
class Participant < ParticipantProxy
|
179
231
|
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
232
|
+
def initialize(options)
|
233
|
+
puts '=' * 80
|
234
|
+
puts "RuoteAMQP::Participant will be deprecated soon (2.1.12)"
|
235
|
+
puts "please use RuoteAMQP::ParticipantProxy instead"
|
236
|
+
puts '=' * 80
|
237
|
+
super
|
184
238
|
end
|
185
239
|
end
|
186
240
|
end
|
241
|
+
|