ruote-stomp-maestrodev 2.2.2

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,146 @@
1
+ require 'ruote-stomp'
2
+
3
+ module RuoteStomp
4
+
5
+ #
6
+ # = Stomp Receiver
7
+ #
8
+ # Used in conjunction with the RuoteStomp::Participant, the WorkitemListener
9
+ # subscribes to a specific direct exchange and monitors for
10
+ # incoming workitems. It expects workitems to arrive serialized as
11
+ # JSON.
12
+ #
13
+ # == Configuration
14
+ #
15
+ # Stomp configuration is handled by directly manipulating the values of
16
+ # the +Stomp.settings+ hash, as provided by the Stomp gem. No
17
+ # defaults are set by the listener. The only +option+ parsed by
18
+ # the initializer of the workitem listener is the +queue+ key (Hash
19
+ # expected). If no +queue+ key is set, the listener will subscribe
20
+ # to the +ruote_workitems+ direct exchange for workitems, otherwise it will
21
+ # subscribe to the direct exchange provided.
22
+ #
23
+ # == Usage
24
+ #
25
+ # Register the engine or storage with the listener:
26
+ #
27
+ # RuoteStomp::Receiver.new(engine_or_storage)
28
+ #
29
+ # The workitem listener leverages the asynchronous nature of the stomp gem,
30
+ # so no timers are setup when initialized.
31
+ #
32
+ # == Options
33
+ #
34
+ # :queue and :launchitems
35
+ #
36
+ # See the RuoteStomp::Participant docs for information on sending
37
+ # workitems out to remote participants, and have them send replies
38
+ # to the correct direct exchange specified in the workitem
39
+ # attributes.
40
+ #
41
+ class Receiver < Ruote::Receiver
42
+
43
+ attr_reader :queue
44
+
45
+ # Starts a new Receiver
46
+ #
47
+ # Two arguments for this method.
48
+ #
49
+ # The first one should be a Ruote::Engine, a Ruote::Storage or
50
+ # a Ruote::Worker instance.
51
+ #
52
+ # The second one is a hash for options. There are two known options :
53
+ #
54
+ # :queue for setting the queue on which to listen (defaults to
55
+ # 'ruote_workitems').
56
+ #
57
+ # :ignore_disconnect_on_process => true|false (defauts to false)
58
+ # processes the message even if the client has disconnected (use in testing only)
59
+ #
60
+ # The :launchitems option :
61
+ #
62
+ # :launchitems => true
63
+ # # the receiver accepts workitems and launchitems
64
+ # :launchitems => false
65
+ # # the receiver only accepts workitems
66
+ # :launchitems => :only
67
+ # # the receiver only accepts launchitems
68
+ #
69
+ def initialize(engine_or_storage, opts={})
70
+
71
+ super(engine_or_storage)
72
+
73
+ @launchitems = opts[:launchitems]
74
+ ignore_disconnect = opts[:ignore_disconnect_on_process]
75
+
76
+ @queue =
77
+ opts[:queue] ||
78
+ (@launchitems == :only ? '/queue/ruote_launchitems' : '/queue/ruote_workitems')
79
+
80
+ RuoteStomp.start!
81
+
82
+ if opts[:unsubscribe]
83
+ begin
84
+ $stomp.unsubscribe(@queue)
85
+ rescue OnStomp::UnsupportedCommandError => e
86
+ $stderr.puts("Connection does support unsubscribe")
87
+ end
88
+ end
89
+
90
+ $stomp.subscribe(@queue) do |message|
91
+ # Process your message here
92
+ # Your submitted data is in msg.body
93
+ if $stomp.connected? && !ignore_disconnect
94
+ # do nothing, we're going down
95
+ else
96
+ handle(message)
97
+ end
98
+ end
99
+ end
100
+
101
+ def stop
102
+ RuoteStomp.stop!
103
+ end
104
+
105
+ # (feel free to overwrite me)
106
+ #
107
+ def decode_workitem(msg)
108
+ (Rufus::Json.decode(msg) rescue nil)
109
+ end
110
+
111
+ private
112
+
113
+ def handle(msg)
114
+ item = decode_workitem(msg.body)
115
+ return unless item.is_a?(Hash)
116
+ not_li = ! item.has_key?('definition')
117
+ return if @launchitems == :only && not_li
118
+ return unless @launchitems || not_li
119
+
120
+ if not_li
121
+ receive(item) # workitem resumes in its process instance
122
+ else
123
+ launch(item) # new process instance launch
124
+ end
125
+
126
+ rescue => e
127
+ # something went wrong
128
+ # let's simply discard the message
129
+ $stderr.puts('=' * 80)
130
+ $stderr.puts(self.class.name)
131
+ $stderr.puts("couldn't handle incoming message :")
132
+ $stderr.puts('')
133
+ $stderr.puts(msg.inspect)
134
+ $stderr.puts('')
135
+ $stderr.puts(Rufus::Json.pretty_encode(item)) rescue nil
136
+ $stderr.puts('')
137
+ $stderr.puts(e.inspect)
138
+ $stderr.puts(e.backtrace)
139
+ $stderr.puts('=' * 80)
140
+ end
141
+
142
+ def launch(hash)
143
+ super(hash['definition'], hash['fields'] || {}, hash['variables'] || {})
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,4 @@
1
+ module RuoteStomp
2
+ VERSION = '2.2.2'
3
+ end
4
+
@@ -0,0 +1,11 @@
1
+ module RuoteStomp
2
+
3
+ #
4
+ # Got replaced by RuoteStomp::Receiver
5
+ #
6
+ # This class is kept for backward compatibility.
7
+ #
8
+ class WorkitemListener < ::RuoteStomp::Receiver
9
+ end
10
+ end
11
+
@@ -0,0 +1,31 @@
1
+ # encoding: utf-8
2
+
3
+ Gem::Specification.new do |s|
4
+
5
+ s.name = 'ruote-stomp-maestrodev'
6
+ s.version = File.read('lib/ruote-stomp/version.rb').match(/VERSION = '([^']+)'/)[1]
7
+ s.platform = Gem::Platform::RUBY
8
+ s.authors = [ 'Kit Plummer', 'Brian Sam-Bodden' ]
9
+ s.email = [ 'kplummer@maestrodev.com', 'bsbodden@integrallis.com' ]
10
+ s.homepage = 'http://ruote.rubyforge.org'
11
+ s.rubyforge_project = 'ruote'
12
+ s.summary = 'Stomp participant/listener pair for ruote 2.2'
13
+ s.description = 'Stomp participant/listener pair for ruote 2.2'
14
+
15
+ s.files = Dir[
16
+ 'Rakefile',
17
+ 'lib/**/*.rb', 'spec/**/*.rb', 'test/**/*.rb',
18
+ '*.gemspec', '*.txt', '*.rdoc', '*.md'
19
+ ]
20
+
21
+ s.add_runtime_dependency 'onstomp', "~> 1.0.4"
22
+ s.add_runtime_dependency 'ruote', "~> 2.2.0"
23
+ s.add_runtime_dependency 'json'
24
+ s.add_runtime_dependency 'parslet'
25
+ s.add_development_dependency 'rake'
26
+ s.add_development_dependency 'rspec', ">= 2.6.0"
27
+ s.add_development_dependency 'stompserver', '~> 0.9.9'
28
+
29
+ s.require_path = 'lib'
30
+ end
31
+
@@ -0,0 +1,78 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ describe RuoteStomp::LaunchitemListener do
4
+
5
+ after(:each) do
6
+ purge_engine
7
+ end
8
+
9
+ it 'launches processes' do
10
+
11
+ json = {
12
+ 'definition' => %{
13
+ Ruote.process_definition :name => 'test' do
14
+ sequence do
15
+ echo '${f:foo}'
16
+ end
17
+ end
18
+ },
19
+ 'fields' => { 'foo' => 'bar' }
20
+ }.to_json
21
+
22
+ RuoteStomp::LaunchitemListener.new(@engine, :ignore_disconnect_on_process => true)
23
+
24
+ finished_processing = false
25
+
26
+ $stomp.send('/queue/ruote_launchitems', json) do |r|
27
+ finished_processing = true
28
+ end
29
+
30
+ begin
31
+ Timeout::timeout(10) do
32
+ while @tracer.to_s.empty?
33
+ print "*"
34
+ sleep 1
35
+ end
36
+ end
37
+ rescue Timeout::Error
38
+ fail "Timeout waiting for message"
39
+ end
40
+
41
+ Thread.pass until finished_processing
42
+
43
+ @engine.should_not have_errors
44
+ @engine.should_not have_remaining_expressions
45
+ @tracer.to_s.should == 'bar'
46
+ end
47
+
48
+ it 'discards corrupt process definitions' do
49
+
50
+ json = {
51
+ 'definition' => %{
52
+ I'm a broken process definition
53
+ },
54
+ 'fields' => { 'foo' => 'bar' }
55
+ }.to_json
56
+
57
+ RuoteStomp::LaunchitemListener.new(@engine, {:unsubscribe => true, :ignore_disconnect_on_process => true})
58
+
59
+ serr = String.new
60
+ err = StringIO.new(serr, 'w+')
61
+ $stderr = err
62
+
63
+ $stomp.send '/queue/ruote_launchitems', json
64
+
65
+ sleep 0.5
66
+
67
+ err.close
68
+ $stderr = STDERR
69
+
70
+ @engine.should_not have_errors
71
+ @engine.should_not have_remaining_expressions
72
+
73
+ @tracer.to_s.should == ''
74
+
75
+ serr.should match(/^===/)
76
+ end
77
+ end
78
+
@@ -0,0 +1,204 @@
1
+
2
+ require File.join(File.dirname(__FILE__), 'spec_helper')
3
+
4
+
5
+ describe RuoteStomp::ParticipantProxy, :type => :ruote do
6
+
7
+ after(:each) do
8
+ purge_engine
9
+ end
10
+
11
+ it "supports 'forget' as participant attribute" do
12
+
13
+ pdef = ::Ruote.process_definition :name => 'test' do
14
+ sequence do
15
+ stomp :queue => '/queue/test1', :forget => true
16
+ echo 'done.'
17
+ end
18
+ end
19
+
20
+ @engine.register_participant(:stomp, RuoteStomp::ParticipantProxy)
21
+
22
+ run_definition(pdef)
23
+
24
+ @tracer.to_s.should == 'done.'
25
+
26
+ begin
27
+ Timeout::timeout(10) do
28
+ @msg = nil
29
+ $stomp.subscribe("/queue/test1") do |message|
30
+ @msg = message.body
31
+ end
32
+
33
+ loop do
34
+ break unless @msg.nil?
35
+ sleep 0.1
36
+ end
37
+ end
38
+ rescue Timeout::Error
39
+ fail "Timeout waiting for message"
40
+ end
41
+
42
+ @msg.should match(/^\{.*\}$/) # JSON message by default
43
+ end
44
+
45
+ it "supports 'forget' as participant option" do
46
+
47
+ pdef = ::Ruote.process_definition :name => 'test' do
48
+ sequence do
49
+ stomp :queue => '/queue/test4'
50
+ echo 'done.'
51
+ end
52
+ end
53
+
54
+ @engine.register_participant(
55
+ :stomp, RuoteStomp::ParticipantProxy, 'forget' => true)
56
+
57
+ run_definition(pdef)
58
+
59
+ @tracer.to_s.should == "done."
60
+
61
+ begin
62
+ Timeout::timeout(5) do
63
+ @msg = nil
64
+ $stomp.subscribe("/queue/test4", {}) do |message|
65
+ @msg = message.body
66
+ end
67
+
68
+ loop do
69
+ break unless @msg.nil?
70
+ sleep 0.1
71
+ end
72
+ end
73
+ rescue Timeout::Error
74
+ fail "Timeout waiting for message"
75
+ end
76
+
77
+ @msg.should match(/^\{.*\}$/) # JSON message by default
78
+ end
79
+
80
+ it "supports custom messages instead of workitems" do
81
+
82
+ pdef = ::Ruote.process_definition :name => 'test' do
83
+ sequence do
84
+ stomp :queue => '/queue/test2', :message => 'foo', :forget => true
85
+ echo 'done.'
86
+ end
87
+ end
88
+
89
+ @engine.register_participant(:stomp, RuoteStomp::ParticipantProxy)
90
+
91
+ run_definition(pdef)
92
+
93
+ @tracer.to_s.should == "done."
94
+
95
+ begin
96
+ Timeout::timeout(5) do
97
+ @msg = nil
98
+ $stomp.subscribe("/queue/test2") do |message|
99
+ @msg = message.body
100
+ end
101
+ loop do
102
+ break unless @msg.nil?
103
+ sleep 0.1
104
+ end
105
+ end
106
+ rescue Timeout::Error
107
+ fail "Timeout waiting for message"
108
+ end
109
+
110
+ @msg.should == 'foo'
111
+ end
112
+
113
+ it "supports 'queue' as a participant option" do
114
+
115
+ pdef = ::Ruote.process_definition :name => 'test' do
116
+ sequence do
117
+ stomp :forget => true
118
+ echo 'done.'
119
+ end
120
+ end
121
+
122
+ @engine.register_participant(
123
+ :stomp, RuoteStomp::ParticipantProxy, 'queue' => '/queue/test5')
124
+
125
+ run_definition(pdef)
126
+
127
+ @tracer.to_s.should == 'done.'
128
+
129
+ begin
130
+ Timeout::timeout(5) do
131
+ @msg = nil
132
+ $stomp.subscribe("/queue/test5") do |message|
133
+ @msg = message.body
134
+ end
135
+ loop do
136
+ break unless @msg.nil?
137
+ sleep 0.1
138
+ end
139
+ end
140
+ rescue Timeout::Error
141
+ fail "Timeout waiting for message"
142
+ end
143
+ end
144
+
145
+ it "passes 'participant_options' over stomp" do
146
+
147
+ pdef = ::Ruote.process_definition :name => 'test' do
148
+ stomp :queue => '/queue/stomp', :forget => true
149
+ end
150
+
151
+ @engine.register_participant(:stomp, RuoteStomp::ParticipantProxy)
152
+
153
+ run_definition(pdef)
154
+
155
+ msg = nil
156
+
157
+ begin
158
+ Timeout::timeout(10) do
159
+
160
+ #MQ.queue('test6', :durable => true).subscribe { |m| msg = m }
161
+ $stomp.subscribe("/queue/stomp") do |message|
162
+ msg = message.body
163
+ end
164
+ loop do
165
+ break unless msg.nil?
166
+ sleep 0.1
167
+ end
168
+ end
169
+ rescue Timeout::Error
170
+ fail "Timeout waiting for message"
171
+ end
172
+
173
+ wi = Rufus::Json.decode(msg)
174
+ params = wi['fields']['params']
175
+
176
+ params['queue'].should == '/queue/stomp'
177
+ params['forget'].should == true
178
+ params['participant_options'].should == { 'forget' => false, 'queue' => nil }
179
+ end
180
+
181
+ # it "doesn't create 1 queue instance per delivery" do
182
+ #
183
+ # pdef = ::Ruote.process_definition do
184
+ # stomp :queue => 'test7', :forget => true
185
+ # end
186
+ #
187
+ # mq_count = 0
188
+ # ObjectSpace.each_object($stomp) { |o| stomp_count += 1 }
189
+ #
190
+ # @engine.register_participant(:stomp, RuoteStomp::ParticipantProxy)
191
+ #
192
+ # 10.times do
193
+ # run_definition(pdef)
194
+ # end
195
+ #
196
+ # sleep 1
197
+ #
198
+ # count = 0
199
+ # ObjectSpace.each_object($stomp) { |o| count += 1 }
200
+ #
201
+ # count.should == stomp_count + 1
202
+ # end
203
+ end
204
+