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.
data/CHANGELOG.txt ADDED
@@ -0,0 +1,11 @@
1
+
2
+ = ruote-stomp
3
+
4
+
5
+ == ruote-stomp - 2.2.0 released ?
6
+
7
+ - work has just begun, porting from ruote-amqp
8
+
9
+
10
+
11
+
data/CREDITS.txt ADDED
@@ -0,0 +1,33 @@
1
+
2
+ = CREDITS
3
+
4
+ (probably incomplete, don't hesitate to tell us if a mention is missing)
5
+
6
+ == AUTHORS
7
+
8
+ * Kit Plummer - http://kitplummer.github.com
9
+ * Brian Sam-Bodden = http://bsbodden.github.com
10
+
11
+ == AUTHORS (from ruote-amqp)
12
+
13
+ * Kenneth Kalmer - http://www.opensourcery.co.za/
14
+ * John Mettraux - https://github.com/jmettraux
15
+
16
+
17
+ == CONTRIBUTORS (from ruote-amqp)
18
+
19
+ * Mario Camou
20
+ * Sean Johnson - https://github.com/belucid
21
+ * Victor Liu - https://github.com/pennymax
22
+ * weifeng - https://github.com/weifeng365
23
+ * David Greaves - https://github.com/lbt
24
+ * Hartog C. de Mik - https://github.com/coffeeaddict
25
+ * Torsten Schoenebaum - https://github.com/tosch
26
+ * Jordan Ritter - https://github.com/jpr5
27
+ * Charles Magid - https://github.com/ChasManRors
28
+ * Marc Mauger - https://github.com/simianarmy
29
+ * Jason - https://github.com/asm
30
+
31
+
32
+ == FEEDBACK
33
+
data/PostInstall.txt ADDED
@@ -0,0 +1,12 @@
1
+
2
+ For more information on ruote-stomp, see http://github.com/maestrodev/ruote-stomp
3
+
4
+ Please note that this gem requires access to an Stomp broker and a good
5
+ understanding of Stomp and remote participants in general.
6
+
7
+ Join us in #ruote on Freenode or on the openwfe-users Google Group to discuss
8
+ it's uses.
9
+
10
+ You might also want to look at daemon-kit for help with writing external
11
+ participants that communicate with ruote via Stomp.
12
+
data/README.rdoc ADDED
@@ -0,0 +1,82 @@
1
+
2
+ = ruote-stomp
3
+
4
+ * http://github.com/kitplummer/ruote-stomp
5
+
6
+ == DESCRIPTION:
7
+
8
+ ruote-stomp provides a Stomp participant/listener pair that allows you to
9
+ distribute workitems out to Stomp consumers for processing, as well as launching
10
+ processes over Stomp.
11
+
12
+ To learn more about remote participants in ruote please see
13
+ http://ruote.rubyforge.org/part_implementations.html
14
+
15
+ == FEATURES/PROBLEMS:
16
+
17
+ * Flexible participant for sending workitems
18
+ * Flexible receiver for receiving replies
19
+ * Flexible launch item listener for launching processes over Stomp
20
+ * Fully evented (thanks to the onstomp gem)
21
+
22
+ == SYNOPSIS:
23
+
24
+ Please review the code for information (rdocs to be updated soon.)
25
+
26
+ == REQUIREMENTS:
27
+
28
+ * ruote[http://ruote.rubyforge.org] 2.2.0 or later
29
+ * onstomp[https://github.com/meadvillerb/onstomp] 1.0.4 or later
30
+ * a server that supports Stomp (stompserver, ActiveMQ, RabbitMQ)
31
+
32
+ == INSTALL:
33
+
34
+ Please be sure to have read the requirements section above
35
+
36
+ * sudo gem install ruote-stomp
37
+
38
+ == TESTS:
39
+
40
+ To run the tests you need the following requirements met, or the testing environment
41
+ will fail horribly (or simply get stuck without output).
42
+
43
+ === Stomp server
44
+
45
+ I've tested it with stompserver and ActiveMQ.
46
+
47
+ I'll work on getting the Ruby stompserver setup to start/shutdown with the tests.
48
+
49
+ == DAEMON-KIT:
50
+
51
+ Will work on adding Daemon-Kit support for ruote-stomp next. :)
52
+
53
+ == LICENSE:
54
+
55
+ (The MIT License)
56
+
57
+ Copyright (c) 2010-2011 Kit Plummer (and Kenneth Kalmer for
58
+ ruote-amqp, which this works is based on)
59
+
60
+ Permission is hereby granted, free of charge, to any person obtaining
61
+ a copy of this software and associated documentation files (the
62
+ 'Software'), to deal in the Software without restriction, including
63
+ <<<<<<< Updated upstream
64
+ without limitation the rights to use, copy, modify, merge, send,
65
+ distribute, sublicense, and/or sell copies of the Software, and to
66
+ =======
67
+ without limitation the rights to use, copy, modify, merge, publish,
68
+ distribute, sublicense, and/or sell copies of tshe Software, and to
69
+ >>>>>>> Stashed changes
70
+ permit persons to whom the Software is furnished to do so, subject to
71
+ the following conditions:
72
+
73
+ The above copyright notice and this permission notice shall be
74
+ included in all copies or substantial portions of the Software.
75
+
76
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
77
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
78
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
79
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
80
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
81
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
82
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,101 @@
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/task'
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 ADDED
@@ -0,0 +1,4 @@
1
+
2
+ [ ] have a class method ParticipantProxy.stop_all ?
3
+ [ ] use Ruote::Workitem #as_json and #from_json(s)
4
+
@@ -0,0 +1,116 @@
1
+ require 'onstomp'
2
+
3
+ require 'ruote-stomp/version'
4
+
5
+ #
6
+ # Stomp participant and listener pair for ruote.
7
+ #
8
+ # == Documentation
9
+ #
10
+ # See #RuoteStomp::Listener and #RuoteStomp::Participant for detailed
11
+ # documentation on using each of them.
12
+ #
13
+ # == Stomp Notes
14
+ #
15
+ # RuoteAmqp uses durable queues and persistent messages by default, to ensure
16
+ # no messages get lost along the way and that running expressions doesn't have
17
+ # to be restarted in order for messages to be resent. <- not exactly sure yet
18
+ # how RuoteStomp will handle durable queues, messages are persisted.
19
+ #
20
+ module RuoteStomp
21
+
22
+ autoload 'ParticipantProxy', 'ruote-stomp/participant'
23
+ autoload 'Receiver', 'ruote-stomp/receiver'
24
+ autoload 'WorkitemListener', 'ruote-stomp/workitem_listener'
25
+ autoload 'LaunchitemListener', 'ruote-stomp/launchitem_listener'
26
+
27
+ class << self
28
+
29
+ attr_writer :use_persistent_messages
30
+
31
+ # Whether or not to use persistent messages (true by default)
32
+ def use_persistent_messages?
33
+ @use_persistent_messages = true if @use_persistent_messages.nil?
34
+ @use_persistent_messages
35
+ end
36
+
37
+ # Ensure the Stomp connection is started
38
+ def start!
39
+ return if started?
40
+ mutex = Mutex.new
41
+ cv = ConditionVariable.new
42
+ Thread.main[:ruote_stomp_connection] = Thread.new do
43
+ Thread.abort_on_exception = true
44
+
45
+ begin
46
+ if STOMP.settings[:ssl]
47
+ $stomp = OnStomp::Client.new(create_connection_uri(STOMP.settings),
48
+ :ssl => {
49
+ :ca_file => STOMP.settings[:cert],
50
+ :verify_mode => OpenSSL::SSL::VERIFY_NONE
51
+ }
52
+ )
53
+ $stomp.connect
54
+ else
55
+ $stomp = OnStomp.connect create_connection_uri(STOMP.settings)
56
+ end
57
+
58
+ if $stomp && $stomp.connected?
59
+ started!
60
+ cv.signal
61
+ end
62
+ rescue Exception => e
63
+ raise RuntimeError, "Failed to connect to Stomp server. (#{e.message})"
64
+ end
65
+ end
66
+
67
+ mutex.synchronize { cv.wait(mutex) }
68
+
69
+ yield if block_given?
70
+ end
71
+
72
+ # Check whether the AMQP connection is started
73
+ def started?
74
+ Thread.main[:ruote_stomp_started] == true
75
+ end
76
+
77
+ def started! #:nodoc:
78
+ Thread.main[:ruote_stomp_started] = true
79
+ end
80
+
81
+ # Close down the Stomp connections
82
+ def stop!
83
+ return unless started?
84
+ $stomp.disconnect
85
+ Thread.main[:ruote_stomp_connection].join
86
+ Thread.main[:ruote_stomp_started] = false
87
+ end
88
+
89
+ protected
90
+
91
+ def create_connection_uri(config={})
92
+ config = config.map { |n,v| {n,v.to_s} }.reduce(:merge)
93
+ user = config[:user]
94
+ passcode = config[:passcode]
95
+ host = config[:host]
96
+ port = config[:port]
97
+ ssl = config[:ssl] || false
98
+ cert = config[:cert] || ""
99
+
100
+ # construct the connection URI
101
+ user_and_password = [user,passcode].reject{|e| e.nil? || e.empty?}.join(":")
102
+ host_and_port = [host,port].reject{|e| e.nil? || e.empty?}.join(":")
103
+ uri = [host_and_port, user_and_password].reject{|e| e.nil? || e.empty?}.reverse.join("@")
104
+ protocol = ['stomp', ssl, '://'].reject{|e| e.nil? || e.empty?}.join
105
+
106
+ "#{protocol}#{uri}"
107
+ end
108
+ end
109
+ end
110
+
111
+ module STOMP
112
+ def self.settings
113
+ @settings ||= {:host => "localhost", :port => "61613"}
114
+ end
115
+ end
116
+
@@ -0,0 +1,20 @@
1
+ module RuoteStomp
2
+
3
+ #
4
+ # Got replaced by RuoteStomp::Receiver
5
+ #
6
+ # This class is kept for backward compatibility.
7
+ #
8
+ class LaunchitemListener < ::RuoteStomp::Receiver
9
+
10
+ # Start a new LaunchItem listener
11
+ #
12
+ # @param [ Ruote::Engine, Ruote::Storage ] A configured ruote engine or storage instance
13
+ # @param opts :queue / :unsubscribe
14
+ #
15
+ def initialize(engine_or_storage, opts={})
16
+ super(engine_or_storage, opts.merge(:launchitems => :only))
17
+ end
18
+ end
19
+ end
20
+
@@ -0,0 +1,212 @@
1
+ require 'ruote/part/local_participant'
2
+ require 'ruote-stomp'
3
+
4
+ module RuoteStomp
5
+ #
6
+ # = Stomp Participants
7
+ #
8
+ # The RuoteStomp::ParticipantProxy allows you to send workitems (serialized as
9
+ # JSON) or messages to any Stomp queues right from the process
10
+ # definition. When combined with the RuoteStomp::Receiver you can easily
11
+ # leverage an extremely powerful local/remote participant
12
+ # combinations.
13
+ #
14
+ # For local/remote participants The local part of the
15
+ # RuoteStomp::ParticipantProxy relies on the presence of a
16
+ # RuoteStomp::Receiver. Workitems are sent to the remote participant
17
+ # and the local part does not normally reply to the engine. Instead
18
+ # the engine will continue when a reply is received on the
19
+ # 'ruote_workitems' queue (see RuoteStomp::Receiver).
20
+ #
21
+ # Of course, the standard :forget => true format can be used even
22
+ # with remote particpants and :forget can even be set as a default in
23
+ # the options.
24
+ #
25
+ # [NOTE: Working this port next!!!]
26
+ # A simple way to create a remote participant to act upon workitems
27
+ # is to use the daemon-kit ruote responder.
28
+ #
29
+ # Simple Stomp messages are treated as 'fire and forget' and the flow
30
+ # will continue when the local participant has queued the message
31
+ # for sending. (As there is no meaningful way to receive a workitem
32
+ # in reply).
33
+ #
34
+ # == Configuration
35
+ #
36
+ # Stomp configuration is handled by directly manipulating the
37
+ # values of the +Stomp.settings+ hash, as provided by the Stomp
38
+ # gem. No Stomp defaults are set by the participant.
39
+ #
40
+ # == Usage
41
+ #
42
+ # Define the queue used by an AMQP participant :
43
+ #
44
+ # engine.register_participant(
45
+ # :delete_user, RuoteStomp::ParticipantProxy, 'queue' => 'user_manager')
46
+ #
47
+ # Sending a workitem to the remote participant defined above:
48
+ #
49
+ # Ruote.process_definition do
50
+ # sequence do
51
+ # delete_user
52
+ # end
53
+ # end
54
+ #
55
+ # Let the local participant reply to the engine without involving
56
+ # the receiver
57
+ #
58
+ # Ruote.process_definition do
59
+ # sequence do
60
+ # delete_user :forget => true
61
+ # end
62
+ # end
63
+ #
64
+ # Setting up the participant in a slightly more 'raw' way:
65
+ #
66
+ # engine.register_participant(
67
+ # :stomp, RuoteStomp::ParticipantProxy )
68
+ #
69
+ # Sending a workitem to a specific queue:
70
+ #
71
+ # Ruote.process_definition do
72
+ # sequence do
73
+ # stomp :queue => 'test', 'command' => '/run/regression_test'
74
+ # end
75
+ # end
76
+ #
77
+ # Setup a 'fire and forget' participant that always replies to the
78
+ # engine:
79
+ #
80
+ # engine.register_participant(
81
+ # :jfdi, RuoteStomp::ParticipantProxy, 'forget' => true )
82
+ #
83
+ # Sending a message example to a specific queue (both steps are
84
+ # equivalent):
85
+ #
86
+ # Ruote.process_definition do
87
+ # sequence do
88
+ # stomp :queue => 'test', :message => 'foo'
89
+ # stomp :queue => 'test', :message => 'foo', :forget => true
90
+ # end
91
+ # end
92
+ #
93
+ #
94
+ # == Stomp notes
95
+ #
96
+ # The direct exchanges are always marked as durable by the
97
+ # participant, and messages are marked as persistent by default (see
98
+ # #RuoteStomp)
99
+ #
100
+ class ParticipantProxy
101
+
102
+ include Ruote::LocalParticipant
103
+
104
+ # The following parameters are used in the process definition.
105
+ #
106
+ # An options hash with the same keys to provide defaults is
107
+ # accepted at registration time (see above).
108
+ #
109
+ # * :queue => (string) The Stomp queue used by the remote participant.
110
+ # nil by default.
111
+ # * :forget => (bool) Whether the flow should block until the remote
112
+ # participant replies.
113
+ # false by default
114
+ #
115
+ def initialize(options)
116
+ @options = {
117
+ 'queue' => nil,
118
+ 'forget' => false,
119
+ }.merge(options.inject({}) { |h, (k, v)|
120
+ h[k.to_s] = v; h
121
+ })
122
+ #
123
+ # the inject is here to make sure that all options have String keys
124
+ end
125
+
126
+ # Process the workitem at hand. By default the workitem will be
127
+ # sended to the direct exchange specified in the +queue+
128
+ # workitem parameter. You can specify a +message+ workitem
129
+ # parameter to have that sent instead of the workitem.
130
+ #
131
+ def consume(workitem)
132
+
133
+ RuoteStomp.start!
134
+ target_queue = determine_queue(workitem)
135
+
136
+ raise 'no queue specified (outbound delivery)' unless target_queue
137
+
138
+ forget = determine_forget(workitem)
139
+
140
+ opts = {
141
+ :persistent => RuoteStomp.use_persistent_messages?,
142
+ :content_type => 'application/json' }
143
+
144
+ if message = workitem.fields['message'] || workitem.params['message']
145
+
146
+ forget = true # sending a message implies 'forget' => true
147
+ $stomp.send target_queue, message, opts
148
+ else
149
+ $stomp.send target_queue, encode_workitem(workitem), opts
150
+ end
151
+
152
+ reply_to_engine(workitem) if forget
153
+ end
154
+
155
+ # (Stops the underlying queue subscription)
156
+ #
157
+ def stop
158
+ RuoteStomp.stop!
159
+ end
160
+
161
+ def cancel(fei, flavour)
162
+ #
163
+ # TODO : sending a cancel item is not a bad idea, especially if the
164
+ # job done over the stomp fence lasts...
165
+ #
166
+ end
167
+
168
+ # [NOT sure about this behavior with Stomp yet. Need to dive.]
169
+
170
+ def do_not_thread
171
+ true
172
+ end
173
+
174
+ private
175
+
176
+ def determine_forget(workitem)
177
+ return workitem.params['forget'] if workitem.params.has_key?('forget')
178
+ return @options['forget'] if @options.has_key?('forget')
179
+ false
180
+ end
181
+
182
+ def determine_queue(workitem)
183
+ workitem.params['queue'] || @options['queue']
184
+ end
185
+
186
+ # Encodes the workitem as JSON. Makes sure to add to the field 'params'
187
+ # an entry named 'participant_options' which contains the options of
188
+ # this participant.
189
+ #
190
+ def encode_workitem(wi)
191
+ wi.params['participant_options'] = @options
192
+ Rufus::Json.encode(wi.to_h)
193
+ end
194
+ end
195
+
196
+ #
197
+ # Kept for backward compatibility.
198
+ #
199
+ # You should use RuoteStomp::ParticipantProxy.
200
+ #
201
+ class Participant < ParticipantProxy
202
+
203
+ def initialize(options)
204
+ puts '=' * 80
205
+ puts "RuoteStomp::Participant will be deprecated soon (2.1.12)"
206
+ puts "please use RuoteStomp::ParticipantProxy instead"
207
+ puts '=' * 80
208
+ super
209
+ end
210
+ end
211
+ end
212
+