ruote-stomp-maestrodev 2.2.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+