ruote-amqp 0.9.21.1 → 2.1.5

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ *.swp
2
+ work/
3
+ logs/
4
+ pkg/
5
+ .yardoc
6
+ doc/
data/History.txt CHANGED
@@ -1,3 +1,10 @@
1
+ === 2.0.0 (WIP)
2
+
3
+ * Compatible with ruote 2.0
4
+ * Thanks to John Mettraux (http://github.com/jmettraux/ruote-amqp)
5
+ * Thanks to Jason & Jordan (http://github.com/asm/ruote-amqp)
6
+ * Thanks to Charles Magid (http://github.com/ChasManRors/ruote-amqp)
7
+
1
8
  === 0.9.21.1 2009-08-03
2
9
 
3
10
  * Switch to using persistent AMQP messages by default
data/Manifest.txt CHANGED
@@ -5,7 +5,8 @@ README.rdoc
5
5
  Rakefile
6
6
  TODO.txt
7
7
  lib/ruote-amqp.rb
8
- lib/ruote-amqp/listener.rb
8
+ lib/ruote-amqp/launchitem_listener.rb
9
+ lib/ruote-amqp/workitem_listener.rb
9
10
  lib/ruote-amqp/participant.rb
10
11
  lib/spec/ruote.rb
11
12
  lib/spec/ruote_example_group.rb
@@ -14,7 +15,8 @@ lib/spec/ruote_matchers.rb
14
15
  script/console
15
16
  script/destroy
16
17
  script/generate
17
- spec/listener_spec.rb
18
+ spec/launchitem_listener_spec.rb
19
+ spec/workitem_listener_spec.rb
18
20
  spec/participant_spec.rb
19
21
  spec/ruote_amqp_spec.rb
20
22
  spec/spec.opts
data/README.rdoc CHANGED
@@ -1,54 +1,58 @@
1
1
  = ruote-amqp
2
2
 
3
3
  * http://github.com/kennethkalmer/ruote-amqp
4
- * http://openwfe.rubyforge.org
4
+ * http://rdoc.info/projects/kennethkalmer/ruote-amqp
5
+ * http://ruote.rubyforge.org
5
6
 
6
7
  == DESCRIPTION:
7
8
 
8
9
  ruote-amqp provides an AMQP participant/listener pair that allows you to
9
- distribute workitems out to AMQP consumers for processing.
10
+ distribute workitems out to AMQP consumers for processing, as well as launching
11
+ processes over AMQP.
10
12
 
11
13
  To learn more about remote participants in ruote please see
12
- http://openwfe.rubyforge.org/part.html
14
+ http://ruote.rubyforge.org/part_implementations.html
13
15
 
14
16
  == FEATURES/PROBLEMS:
15
17
 
16
18
  * Flexible participant for sending workitems
17
19
  * Flexible listener for receiving replies
20
+ * Flexible launch item listener for launching processes over AMQP
18
21
  * Fully evented (thanks to the amqp gem)
19
22
 
20
23
  == SYNOPSIS:
21
24
 
22
- Please review the detailed RDOC in RuoteAMQP::Participant and Ruote::AMQP::Listener
25
+ Please review the rdoc in RuoteAMQP::Participant and Ruote::AMQP::Listener
23
26
 
24
27
  == REQUIREMENTS:
25
28
 
26
- * ruote[http://openwfe.rubyforge.org] 0.9.21 or later
27
- * amqp[http://github.com/tmm1/amqp] 0.6.1 or later
29
+ * ruote[http://ruote.rubyforge.org] 2.1.4 or later
30
+ * amqp[http://github.com/tmm1/amqp] 0.6.6 or later
31
+ * rufus-json[http://github.com/jmettraux/rufus-json] 0.1.0 or later
32
+ * rabbitmq[http://www.rabbitmq.com/] 1.6.0 or later
28
33
 
29
- NOTE: It might be required that you build the amqp gem yourself.
34
+ == INSTALL:
30
35
 
31
- ruote 0.9.21 is currently only available to build on your own since John
32
- Mettraux is working tirelessly to ship ruote 2.0. To build your own ruote
33
- 0.9.21 gem run these commands:
36
+ Please be sure to have read the requirements section above
34
37
 
35
- $ git clone git://github.com/jmettraux/ruote.git
36
- $ cd ruote
37
- $ rake gem
38
- $ sudo gem install pkg/ruote-0.9.21.gem
38
+ * sudo gem install ruote-amqp
39
39
 
40
- Please note that this requires Rubygems 1.3.2 or newer to work
40
+ == TESTS:
41
41
 
42
- If you're using ruote 0.9.20 and would like to use ruote-amqp, you can install
43
- ruote-amqp-0.9.20, like so:
42
+ To run the tests you need the following requirements met, or the testing environment
43
+ will fail horribly.
44
44
 
45
- $ sudo gem install ruote-amqp -v 0.9.20
45
+ === RabbitMQ vhost
46
46
 
47
- == INSTALL:
47
+ The tests use dedicated vhost on a running AMQP broker. To configure RabbitMQ
48
+ you can run the following commands:
48
49
 
49
- Please be sure to have read the requirements section above
50
+ # rabbitmqctl add_vhost ruote-test
51
+ # rabbitmqctl add_user ruote ruote
52
+ # rabbitmqctl set_permissions -p ruote-test ruote '.*' '.*' '.*'
50
53
 
51
- * sudo gem install ruote-amqp
54
+ If you need to change the AMQP configuration used by the tests, edit the
55
+ +spec/spec_helper.rb+ file.
52
56
 
53
57
  == DAEMON-KIT:
54
58
 
@@ -59,11 +63,13 @@ to write your remote participants.
59
63
  daemon-kit offers plenty of convenience for remote participants and includes
60
64
  a code generator for ruote remote participants.
61
65
 
66
+ DaemonKit doesn't currently support ruote 2.1, support is forthcoming.
67
+
62
68
  == LICENSE:
63
69
 
64
70
  (The MIT License)
65
71
 
66
- Copyright (c) 2009 Kenneth Kalmer
72
+ Copyright (c) 2010 Kenneth Kalmer
67
73
 
68
74
  Permission is hereby granted, free of charge, to any person obtaining
69
75
  a copy of this software and associated documentation files (the
data/Rakefile CHANGED
@@ -1,26 +1,50 @@
1
1
  require 'rubygems'
2
- gem 'hoe', '>= 2.1.0'
3
- require 'hoe'
4
- require 'fileutils'
5
- require './lib/ruote-amqp'
6
-
7
- Hoe.plugin :newgem
8
- # Hoe.plugin :website
9
- # Hoe.plugin :cucumberfeatures
10
-
11
- # Generate all the Rake tasks
12
- # Run 'rake -T' to see list of generated tasks (from gem root directory)
13
- $hoe = Hoe.spec 'ruote-amqp' do
14
- self.developer 'Kenneth Kalmer', 'kenneth.kalmer@gmail.com'
15
- self.post_install_message = 'PostInstall.txt' # TODO remove if post-install message not required
16
- self.rubyforge_name = self.name # TODO this is default value
17
- self.extra_deps = [['ruote','= 0.9.20'], ['amqp', '= 0.6.0']]
2
+ require 'rake'
18
3
 
4
+ require 'lib/ruote-amqp'
5
+
6
+ begin
7
+ require 'jeweler'
8
+ Jeweler::Tasks.new do |gemspec|
9
+ gemspec.name = 'ruote-amqp'
10
+ gemspec.version = RuoteAMQP::VERSION
11
+ gemspec.summary = 'AMQP participant/listener pair for ruote 2.1'
12
+ gemspec.email = 'kenneth.kalmer@gmail.com'
13
+ gemspec.homepage = 'http://github.com/kennethkalmer/ruote-amqp'
14
+ gemspec.authors = ['kenneth.kalmer@gmail.com']
15
+ gemspec.extra_rdoc_files.include '*.txt'
16
+
17
+ gemspec.add_dependency 'rufus-json', '>= 0.1.0'
18
+ gemspec.add_dependency 'amqp', '>= 0.6.6'
19
+ gemspec.add_dependency 'ruote', '>= 2.1.5'
20
+ gemspec.add_development_dependency 'rspec'
21
+ end
22
+ Jeweler::GemcutterTasks.new
23
+ rescue LoadError
24
+ puts "Jeweler not available. Install it with 'gem install jeweler'"
25
+ end
26
+
27
+ require 'spec/rake/spectask'
28
+ Spec::Rake::SpecTask.new(:spec) do |spec|
29
+ spec.libs << 'lib' << 'spec'
30
+ spec.spec_files = FileList['spec/**/*_spec.rb']
19
31
  end
20
32
 
21
- require 'newgem/tasks'
22
- Dir['tasks/**/*.rake'].each { |t| load t }
33
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
34
+ spec.libs << 'lib' << 'spec'
35
+ spec.pattern = 'spec/**/*_spec.rb'
36
+ spec.rcov = true
37
+ end
38
+
39
+ task :spec #=> :check_dependencies
23
40
 
24
- # TODO - want other tests/tasks run by default? Add them to the list
25
- # remove_task :default
26
- # task :default => [:spec, :features]
41
+ task :default => :spec
42
+
43
+ begin
44
+ require 'yard'
45
+ YARD::Rake::YardocTask.new
46
+ rescue LoadError
47
+ task :yardoc do
48
+ abort "YARD is not available. In order to run yardoc, you must: sudo gem install yard"
49
+ end
50
+ end
data/lib/ruote-amqp.rb CHANGED
@@ -1,20 +1,3 @@
1
- $:.unshift(File.dirname(__FILE__)) unless
2
- $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
-
4
- begin
5
- require 'openwfe'
6
- rescue LoadError
7
- require 'rubygems'
8
- gem 'ruote', '>= 0.9.21'
9
- require 'openwfe'
10
- end
11
- require 'openwfe/version'
12
-
13
- if OpenWFE::OPENWFERU_VERSION < '0.9.21'
14
- raise "ruote-amqp requires at least ruote-0.9.21"
15
- end
16
-
17
- require 'yaml'
18
1
  require 'mq'
19
2
 
20
3
  # AMQP participant and listener pair for ruote.
@@ -31,10 +14,12 @@ require 'mq'
31
14
  # to be restarted in order for messages to be resent.
32
15
  #
33
16
  module RuoteAMQP
34
- VERSION = '0.9.21.1'
35
17
 
36
- autoload 'Participant', 'ruote-amqp/participant'
37
- autoload 'Listener', 'ruote-amqp/listener'
18
+ VERSION = '2.1.5'
19
+
20
+ autoload 'Participant', 'ruote-amqp/participant'
21
+ autoload 'WorkitemListener', 'ruote-amqp/workitem_listener'
22
+ autoload 'LaunchitemListener', 'ruote-amqp/launchitem_listener'
38
23
 
39
24
  class << self
40
25
 
@@ -45,5 +30,46 @@ module RuoteAMQP
45
30
  @use_persistent_messages = true if @use_persistent_messages.nil?
46
31
  @use_persistent_messages
47
32
  end
33
+
34
+ # Ensure the AMQP connection is started
35
+ def start!
36
+ return if started?
37
+
38
+ mutex = Mutex.new
39
+ cv = ConditionVariable.new
40
+
41
+ Thread.main[:ruote_amqp_connection] = Thread.new do
42
+ Thread.abort_on_exception = true
43
+ AMQP.start {
44
+ started!
45
+ cv.signal
46
+ }
47
+ end
48
+
49
+ mutex.synchronize { cv.wait(mutex) }
50
+
51
+ MQ.prefetch(1)
52
+
53
+ yield if block_given?
54
+ end
55
+
56
+ # Check whether the AMQP connection is started
57
+ def started?
58
+ Thread.main[:ruote_amqp_started] == true
59
+ end
60
+
61
+ def started! #:nodoc:
62
+ Thread.main[:ruote_amqp_started] = true
63
+ end
64
+
65
+ # Close down the AMQP connections
66
+ def stop!
67
+ return unless started?
68
+
69
+ AMQP.stop
70
+ Thread.main[:ruote_amqp_connection].join
71
+ Thread.main[:ruote_amqp_started] = false
72
+ end
73
+
48
74
  end
49
75
  end
@@ -0,0 +1,88 @@
1
+ module RuoteAMQP
2
+
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
+ #
22
+ # AMQP configuration is handled by directly manipulating the values of the
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.
27
+ #
28
+ # The listener requires version 0.6.6 or later of the amqp gem.
29
+ #
30
+ # == Usage
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
50
+
51
+ # Start a new LaunchItem listener
52
+ #
53
+ # @param [ Ruote::Engine ] An instance of a ruote engine
54
+ # @param [ String ] Optional queue name
55
+ def initialize( engine, queue = nil )
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') || {}
84
+
85
+ [ definition, fields, variables ]
86
+ end
87
+ end
88
+ end
@@ -1,3 +1,5 @@
1
+ require 'ruote/part/local_participant'
2
+
1
3
  module RuoteAMQP
2
4
 
3
5
  # = AMQP Participants
@@ -111,14 +113,15 @@ module RuoteAMQP
111
113
  # #RuoteAMQP)
112
114
  #
113
115
  class Participant
114
- include OpenWFE::LocalParticipant
116
+
117
+ include Ruote::LocalParticipant
115
118
 
116
119
  # Accepts an options hash with the following keys:
117
120
  #
118
121
  # * :reply_by_default => (bool) false by default
119
122
  # * :default_queue => (string) nil by default
120
123
  def initialize( options = {} )
121
- ensure_reactor!
124
+ RuoteAMQP.start!
122
125
 
123
126
  @options = {
124
127
  :reply_by_default => false,
@@ -140,57 +143,44 @@ module RuoteAMQP
140
143
  # To force the participant to reply to the engine, set the
141
144
  # +reply_anyway+ workitem parameter.
142
145
  def consume( workitem )
143
- ldebug { "consuming workitem" }
144
- ensure_reactor!
145
-
146
146
  if target_queue = determine_queue( workitem )
147
147
 
148
148
  q = MQ.queue( target_queue, :durable => true )
149
149
 
150
150
  # Message or workitem?
151
- if message = ( workitem.attributes['message'] || workitem.params['message'] )
152
- ldebug { "sending message to queue: #{target_queue}" }
151
+ if message = ( workitem.fields['message'] || workitem.fields['params']['message'] )
153
152
  q.publish( message, :persistent => RuoteAMQP.use_persistent_messages? )
154
-
155
153
  else
156
- ldebug { "sending workitem to queue: #{target_queue}" }
157
-
158
154
  q.publish( encode_workitem( workitem ), :persistent => RuoteAMQP.use_persistent_messages? )
159
155
  end
160
156
  else
161
- lerror { "no queue in workitem params!" }
157
+ raise "no queue in workitem params!"
162
158
  end
163
159
 
164
- if @options[:reply_by_default] || workitem.params['reply-anyway'] == true
160
+ if @options[:reply_by_default] || workitem.fields['params']['reply_anyway'] == true
165
161
  reply_to_engine( workitem )
166
162
  end
167
-
168
- ldebug { "done" }
169
163
  end
170
164
 
171
165
  def stop
172
- linfo { "Stopping..." }
166
+ RuoteAMQP.stop!
167
+ end
173
168
 
174
- AMQP.stop { EM.stop } #if EM.reactor_running? }
175
- @em_thread.join if @em_thread
169
+ def cancel(fei, flavour)
176
170
  end
177
171
 
178
172
  private
179
173
 
180
174
  def determine_queue( workitem )
181
- workitem.params['queue'] ||
175
+ workitem.fields['params']['queue'] ||
182
176
  @participant_maps[ workitem.participant_name ] ||
183
177
  @options[:default_queue]
184
178
  end
185
179
 
186
180
  # Encode (and extend) the workitem as JSON
187
181
  def encode_workitem( wi )
188
- wi.attributes['reply_queue'] = Listener.queue
189
- OpenWFE::Json.encode( wi.to_h )
190
- end
191
-
192
- def ensure_reactor!
193
- @em_thread = Thread.new { EM.run } unless EM.reactor_running?
182
+ wi.fields['params']['reply_queue'] = WorkitemListener.queue
183
+ wi.to_h.to_json
194
184
  end
195
185
  end
196
186
  end