explainer-rmb-rails 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +5 -0
- data/LICENSE +20 -0
- data/README.rdoc +7 -0
- data/Rakefile +58 -0
- data/VERSION.yml +1 -0
- data/lib/listener_client.rb +58 -0
- data/lib/listener_daemon.rb +19 -0
- data/lib/listener_daemon_control.rb +70 -0
- data/lib/listener_main.rb +83 -0
- data/lib/mechanize_submitter.rb +83 -0
- data/lib/rmb-rails.rb +51 -0
- data/lib/rmb2.rb +69 -0
- data/lib/stomp_subscriber.rb +51 -0
- data/lib/submitter.rb +22 -0
- data/lib/subscriber.rb +22 -0
- data/rmb-rails.gemspec +57 -0
- data/test/rmb-rails_test.rb +7 -0
- data/test/test_helper.rb +9 -0
- metadata +73 -0
data/.document
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Ken Burgett
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "rmb-rails"
|
8
|
+
gem.summary = %Q{RESTful Message Beans for Rails}
|
9
|
+
gem.email = "keburgett@gmail.com"
|
10
|
+
gem.homepage = "http://github.com/explainer/rmb-rails"
|
11
|
+
gem.authors = ["Ken Burgett"]
|
12
|
+
gem.rubyforge_project = "rmb-rails"
|
13
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
14
|
+
end
|
15
|
+
|
16
|
+
Jeweler::RubyforgeTasks.new
|
17
|
+
rescue LoadError
|
18
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
19
|
+
end
|
20
|
+
|
21
|
+
require 'rake/testtask'
|
22
|
+
Rake::TestTask.new(:test) do |test|
|
23
|
+
test.libs << 'lib' << 'test'
|
24
|
+
test.pattern = 'test/**/*_test.rb'
|
25
|
+
test.verbose = true
|
26
|
+
end
|
27
|
+
|
28
|
+
begin
|
29
|
+
require 'rcov/rcovtask'
|
30
|
+
Rcov::RcovTask.new do |test|
|
31
|
+
test.libs << 'test'
|
32
|
+
test.pattern = 'test/**/*_test.rb'
|
33
|
+
test.verbose = true
|
34
|
+
end
|
35
|
+
rescue LoadError
|
36
|
+
task :rcov do
|
37
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
task :default => :test
|
43
|
+
|
44
|
+
require 'rake/rdoctask'
|
45
|
+
Rake::RDocTask.new do |rdoc|
|
46
|
+
if File.exist?('VERSION.yml')
|
47
|
+
config = YAML.load(File.read('VERSION.yml'))
|
48
|
+
version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
|
49
|
+
else
|
50
|
+
version = ""
|
51
|
+
end
|
52
|
+
|
53
|
+
rdoc.rdoc_dir = 'rdoc'
|
54
|
+
rdoc.title = "rmb-rails #{version}"
|
55
|
+
rdoc.rdoc_files.include('README*')
|
56
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
57
|
+
end
|
58
|
+
|
data/VERSION.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.4
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module RMB
|
2
|
+
|
3
|
+
#
|
4
|
+
# This code is called from the client application. It starts/stops the daemon controller script
|
5
|
+
#
|
6
|
+
class ListenerClient
|
7
|
+
|
8
|
+
def initialize(hash)
|
9
|
+
@hash = hash
|
10
|
+
end
|
11
|
+
|
12
|
+
def start
|
13
|
+
control('start')
|
14
|
+
end
|
15
|
+
|
16
|
+
def stop
|
17
|
+
control('stop')
|
18
|
+
end
|
19
|
+
|
20
|
+
def control(action)
|
21
|
+
setup
|
22
|
+
control_script = "ruby #{File.dirname(__FILE__)}/#{LD}control.rb #{action}"
|
23
|
+
control_params = "#{@hash[:working_dir]} #{@hash[:key]}"
|
24
|
+
system("#{control_script} #{control_params} -- #{control_params}")
|
25
|
+
end
|
26
|
+
|
27
|
+
def app_name
|
28
|
+
@hash[:daemon_options][:app_name]
|
29
|
+
end
|
30
|
+
|
31
|
+
def properties=(hash)
|
32
|
+
@hash=hash
|
33
|
+
end
|
34
|
+
|
35
|
+
def properties
|
36
|
+
@hash
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def setup
|
42
|
+
# add derived values to the daemon_options hash
|
43
|
+
@hash[:daemon_options][:app_name] = "#{LD}#{@hash[:key]}"
|
44
|
+
@hash[:daemon_options][:dir] = File.join("#{@hash[:working_dir]}", "tmp", "pids")
|
45
|
+
# Ensure the properties folder is present
|
46
|
+
properties_dir = File.join("#{@hash[:working_dir]}", "tmp", "properties")
|
47
|
+
if !File.directory?(properties_dir)
|
48
|
+
Dir.mkdir(properties_dir)
|
49
|
+
end
|
50
|
+
# dump the properties to a tmp file
|
51
|
+
File.open(File.join("#{properties_dir}", "#{app_name}.properties"), "w+") do |f|
|
52
|
+
Marshal.dump(@hash, f)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
#
|
2
|
+
# Listener Daemon main program
|
3
|
+
#
|
4
|
+
|
5
|
+
# Value of ARGV[0] => RAILS_ROOT
|
6
|
+
# Value of ARGV[1] => key
|
7
|
+
|
8
|
+
require 'rubygems'
|
9
|
+
require 'logger'
|
10
|
+
require 'rmb-rails'
|
11
|
+
|
12
|
+
#
|
13
|
+
# start the daemon worker code...
|
14
|
+
#
|
15
|
+
listener = RMB::ListenerMain.new(ARGV[0], ARGV[1])
|
16
|
+
# ...and listen forever
|
17
|
+
listener.run
|
18
|
+
|
19
|
+
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
#
|
3
|
+
# Listener Daemon Control Program
|
4
|
+
#
|
5
|
+
require 'logger'
|
6
|
+
require 'daemons'
|
7
|
+
require 'rmb-rails'
|
8
|
+
#
|
9
|
+
# This ruby script is invoked by the ListenerClient.control method to start and stop the daemon
|
10
|
+
#
|
11
|
+
=begin rdoc
|
12
|
+
+app_name+ The name of the application. This will be used to contruct the name of the pid files and log files.
|
13
|
+
Defaults to the basename of the script.
|
14
|
+
+ARGV+ An array of strings containing parameters and switches for Daemons. This includes both parameters for Daemons
|
15
|
+
itself and the controlling script. These are assumed to be separated by an array element ’—’,
|
16
|
+
e.g. [‘start’, ‘f’, ’—’, ‘param1_for_script’, ‘param2_for_script’].
|
17
|
+
If not given, ARGV (the parameters given to the Ruby process) will be used.
|
18
|
+
+dir_mode+ Either
|
19
|
+
+script (the directory for writing the pid files to given by +dir is interpreted relative to the script location given by script)
|
20
|
+
+normal (the directory given by +dir is interpreted as a (absolute or relative) path)
|
21
|
+
+system (/var/run is used as the pid file directory)
|
22
|
+
+dir+ Used in combination with +dir_mode (description above)
|
23
|
+
+multiple+ Specifies whether multiple instances of the same script are allowed to run at the same time
|
24
|
+
+ontop+ When given (i.e. set to true), stay on top, i.e. do not daemonize the application
|
25
|
+
(but the pid-file and other things are written as usual)
|
26
|
+
+mode+
|
27
|
+
+load Load the script with Kernel.load;
|
28
|
+
+exec Execute the script file with Kernel.exec
|
29
|
+
+backtrace+ Write a backtrace of the last exceptions to the file ’[app_name].log’ in the pid-file
|
30
|
+
directory if the application exits due to an uncaught exception
|
31
|
+
+monitor+ Monitor the programs and restart crashed instances
|
32
|
+
+log_output+ When given (i.e. set to true), redirect both STDOUT and STDERR to a logfile named ’[app_name].output’ in the pid-file directory
|
33
|
+
+keep_pid_files+ When given do not delete lingering pid-files (files for which the process is no longer running).
|
34
|
+
+hard_exit+ When given use exit! to end a daemons instead of exit (this will for example not call at_exit handlers).
|
35
|
+
=end
|
36
|
+
# Value of ARGV[0] => action (start|stop)
|
37
|
+
# Value of ARGV[1] => RAILS_ROOT
|
38
|
+
|
39
|
+
# Value of ARGV[2] => listener key
|
40
|
+
key = ARGV[2]
|
41
|
+
daemon_name = "listener_daemon_#{key}"
|
42
|
+
# Value of ARGV[2] => --
|
43
|
+
# Value of ARGV[3] => listener key
|
44
|
+
|
45
|
+
messages_dir = File.join("#{ARGV[1]}", "tmp", "messages")
|
46
|
+
if !File.directory?(messages_dir)
|
47
|
+
Dir.mkdir(messages_dir)
|
48
|
+
end
|
49
|
+
logger = Logger.new("#{ARGV[1]}/log/listener_daemon_control.log")
|
50
|
+
logger.info "Starting the #{File.basename(__FILE__)}..."
|
51
|
+
0.upto ARGV.length-1 do |i|
|
52
|
+
logger.info "Value of ARGV[#{i}] => #{ARGV[i]}"
|
53
|
+
end
|
54
|
+
|
55
|
+
# load an instance of the ListenerMain in order to get the options hash
|
56
|
+
l = RMB::ListenerMain.new(ARGV[1], ARGV[2])
|
57
|
+
options = l.hash[:daemon_options]
|
58
|
+
options.each do |key, value|
|
59
|
+
logger.info "options[#{key}] => #{value}"
|
60
|
+
end
|
61
|
+
|
62
|
+
# launch the daemon file from the same directory as this program file.
|
63
|
+
target = File.join(File.dirname(__FILE__), "listener_daemon.rb")
|
64
|
+
logger.info "Launching #{target}...\n"
|
65
|
+
begin
|
66
|
+
Daemons.run(target, options)
|
67
|
+
rescue Exception
|
68
|
+
logger.fatal "#{__FILE__}:#{__LINE__} Exception #{$!}"
|
69
|
+
raise
|
70
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module RMB
|
2
|
+
#
|
3
|
+
# This is the class that gets instantiated from the listener daemon
|
4
|
+
# It contains the worker code of the daemon
|
5
|
+
#
|
6
|
+
class ListenerMain
|
7
|
+
attr_accessor :submitter, :subscriber, :logger, :hash
|
8
|
+
=begin rdoc
|
9
|
+
+initialize+ Given root (the working directory or RAILS_ROOT) and key (the unique identifier that denotes a
|
10
|
+
particular Listener instance), the initialize method sets up the entire daemon.
|
11
|
+
* The daemon name is created from the key
|
12
|
+
* A logger object is instantiated for the use of the daemon components
|
13
|
+
* A hash of properties is read from a properties file, whose name is also derived from the root and key.
|
14
|
+
* An instance of a subclass of Subscriber is created to act as the front end of the daemon, listening on a topic or queue for the arrival of a message
|
15
|
+
* An instance of a subclass of Submitter is created to send messages on to a parent rails app, using RESTful techniques to send the message to a rails controller.
|
16
|
+
=end
|
17
|
+
def initialize(root, key)
|
18
|
+
@key = key
|
19
|
+
@logger = Logger.new("#{root}/log/#{app_name}.log")
|
20
|
+
@logger.info "\nStarting #{File.basename(__FILE__)} --> #{app_name}..."
|
21
|
+
@hash = nil
|
22
|
+
# load the marshalled hash of properties
|
23
|
+
f = File.join("#{root}", "tmp", "properties", "#{app_name}.properties")
|
24
|
+
File.open(f) do |props|
|
25
|
+
@hash = Marshal.load(props)
|
26
|
+
end
|
27
|
+
@hash[:logger] = @logger
|
28
|
+
# instantiate the two objects that comprise the front-end and the back-end of
|
29
|
+
# this listener daemon
|
30
|
+
front = @hash[:subscriber]
|
31
|
+
@subscriber = Kernel.eval "#{front[:class_name]}.new"
|
32
|
+
@subscriber.properties=@hash
|
33
|
+
back = @hash[:submitter]
|
34
|
+
@submitter = Kernel.eval "#{back[:class_name]}.new"
|
35
|
+
@submitter.properties=@hash
|
36
|
+
end
|
37
|
+
=begin rdoc
|
38
|
+
+run+
|
39
|
+
* The subscriber is connected to a message broker,
|
40
|
+
* the submitter is connected to the rails controller,
|
41
|
+
* then an infinite loop is entered
|
42
|
+
* subscriber waits to receive a message
|
43
|
+
* when a message arrives, it is sent to the rails controller via the submitter
|
44
|
+
* ...and loop forever
|
45
|
+
=end
|
46
|
+
def run
|
47
|
+
begin
|
48
|
+
subscriber.connect
|
49
|
+
logger.info "subscriber connected."
|
50
|
+
rescue Exception
|
51
|
+
logger.fatal "#{__FILE__}:#{__LINE__} Exception #{$!}"
|
52
|
+
raise
|
53
|
+
end
|
54
|
+
begin
|
55
|
+
submitter.connect
|
56
|
+
logger.info "submitter logged in."
|
57
|
+
rescue Exception
|
58
|
+
logger.fatal "#{__FILE__}:#{__LINE__} Exception #{$!}"
|
59
|
+
raise
|
60
|
+
end
|
61
|
+
logger.info "Waiting for messages in #{subscriber.url}."
|
62
|
+
loop do
|
63
|
+
begin
|
64
|
+
message = subscriber.receive
|
65
|
+
rescue
|
66
|
+
logger.fatal "#{__FILE__}:#{__LINE__} Exception #{$!}"
|
67
|
+
raise
|
68
|
+
end
|
69
|
+
begin
|
70
|
+
submitter.send(message)
|
71
|
+
rescue Exception
|
72
|
+
logger.fatal "#{__FILE__}:#{__LINE__} Exception #{$!}"
|
73
|
+
raise
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def app_name
|
79
|
+
"#{LD}#{@key}"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'mechanize'
|
2
|
+
require 'submitter'
|
3
|
+
|
4
|
+
module RMB
|
5
|
+
|
6
|
+
class MechanizeSubmitter < Submitter
|
7
|
+
attr_accessor :user, :password, :login_url, :delivery_url, :agent, :logger, :agent, :daemon_name
|
8
|
+
=begin rdoc
|
9
|
+
+properties=(hash)+ Accepts a hash object containing all of the configuration properties required. These properties are copied into instance variables.
|
10
|
+
=end
|
11
|
+
def properties=(hash)
|
12
|
+
super
|
13
|
+
@hash = hash
|
14
|
+
@daemon_name = "#{LD}#{hash[:key]}"
|
15
|
+
submitter = hash[:submitter]
|
16
|
+
@user = submitter[:user] || ""
|
17
|
+
@password = submitter[:password] || ""
|
18
|
+
@login_url = submitter[:login_url] || ""
|
19
|
+
@delivery_url = submitter[:delivery_url] || ""
|
20
|
+
begin
|
21
|
+
@agent = WWW::Mechanize.new
|
22
|
+
@agent.user_agent_alias = 'Linux Mozilla'
|
23
|
+
rescue Exception
|
24
|
+
logger.fatal "#{__FILE__}:#{__LINE__} Exception #{$!}"
|
25
|
+
raise
|
26
|
+
end
|
27
|
+
end
|
28
|
+
=begin rdoc
|
29
|
+
+connect+ Uses the login_url property, along with the user and password properties, to log in to the rails app.
|
30
|
+
=end
|
31
|
+
def connect
|
32
|
+
super
|
33
|
+
#this code requires completion of the User controller in the main app
|
34
|
+
end
|
35
|
+
=begin rdoc
|
36
|
+
+marshal_message_body+ Extracts the message body from the rest of the message, and marshals it into a file. The name of this file will be submitted to the rails app, along with other message attributes.
|
37
|
+
=end
|
38
|
+
def marshal_message_body(message)
|
39
|
+
file = File.join("#{@hash[:working_dir]}", "tmp", "messages", "#{daemon_name}_#{message.headers["timestamp"]}.message")
|
40
|
+
File.open(file, "w+") do |f|
|
41
|
+
Marshal.dump(message.body, f)
|
42
|
+
end
|
43
|
+
file
|
44
|
+
end
|
45
|
+
=begin rdoc
|
46
|
+
+send+ This method uses the +delivery_url+ property to issue a get request to retrieve the <tt>new document</tt> form from the rails app. The form is filled in with message attributes and submitted to the rails app for processing.
|
47
|
+
=end
|
48
|
+
def send(message)
|
49
|
+
super
|
50
|
+
file = marshal_message_body(message)
|
51
|
+
begin
|
52
|
+
page = agent.get(delivery_url)
|
53
|
+
rescue Exception
|
54
|
+
logger.fatal "#{__FILE__}:#{__LINE__} Exception #{$!}"
|
55
|
+
raise
|
56
|
+
end
|
57
|
+
form = page.forms.first
|
58
|
+
|
59
|
+
# I can't seem to make the Mechanize code recognize fields as attributes, so
|
60
|
+
# I am forced to treat them as an array
|
61
|
+
form.fields[1].value = @hash[:key]
|
62
|
+
form.fields[2].value = message.headers["destination"]
|
63
|
+
form.fields[3].value = message.headers["message-id"]
|
64
|
+
form.fields[4].value = message.headers["content-type"]
|
65
|
+
form.fields[5].value = message.headers["priority"]
|
66
|
+
form.fields[6].value = message.headers["content-length"]
|
67
|
+
form.fields[7].value = message.headers["timestamp"]
|
68
|
+
form.fields[8].value = message.headers["expires"]
|
69
|
+
form.fields[9].value = file
|
70
|
+
|
71
|
+
#logger.info "final form: #{form.inspect}"
|
72
|
+
|
73
|
+
#submit the form
|
74
|
+
begin
|
75
|
+
page = agent.submit(form)
|
76
|
+
rescue Exception
|
77
|
+
logger.fatal "#{__FILE__}:#{__LINE__} Exception #{$!}"
|
78
|
+
raise
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
data/lib/rmb-rails.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'stomp'
|
2
|
+
require 'subscriber'
|
3
|
+
|
4
|
+
module RMB
|
5
|
+
|
6
|
+
class StompSubscriber < Subscriber
|
7
|
+
attr_accessor :url, :host, :port, :user, :password, :connection, :logger
|
8
|
+
=begin rdoc
|
9
|
+
+connect+ Opens a connection with the message broker, and then subscribes on the url
|
10
|
+
=end
|
11
|
+
def connect
|
12
|
+
super
|
13
|
+
begin
|
14
|
+
@connection = Stomp::Connection.open(user, password, host, port)
|
15
|
+
@connection.subscribe url, { :ack => 'auto' }
|
16
|
+
rescue Exception
|
17
|
+
logger.fatal "#{__FILE__}:#{__LINE__} Exception #{$!}"
|
18
|
+
raise
|
19
|
+
end
|
20
|
+
logger.info "Waiting for messages in #{url}."
|
21
|
+
end
|
22
|
+
=begin rdoc
|
23
|
+
+properties=(hash)+ Accepts a hash object containing all of the configuration properties required. These properties are copied into instance variables.
|
24
|
+
=end
|
25
|
+
def properties=(hash)
|
26
|
+
subscriber = hash[:subscriber]
|
27
|
+
@url = subscriber[:url] || "/queue/something"
|
28
|
+
@host = subscriber[:host] || ""
|
29
|
+
@port = subscriber[:port] || 61613
|
30
|
+
@user = subscriber[:user] || ""
|
31
|
+
@password = subscriber[:password] || ""
|
32
|
+
@connection = nil
|
33
|
+
super
|
34
|
+
end
|
35
|
+
=begin rdoc
|
36
|
+
+receive+ Executes a receive on the connection, thus blocking the daemon until a message arrives. Then answers the message.
|
37
|
+
=end
|
38
|
+
def receive
|
39
|
+
super
|
40
|
+
begin
|
41
|
+
message = @connection.receive
|
42
|
+
rescue Exception
|
43
|
+
logger.fatal "#{__FILE__}:#{__LINE__} Exception #{$!}"
|
44
|
+
raise
|
45
|
+
end
|
46
|
+
logger.info "Received message: #{message.inspect}"
|
47
|
+
message
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
data/lib/rmb2.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
#
|
2
|
+
# = RESTful-Message-Beans module
|
3
|
+
#
|
4
|
+
# Abstraction which wraps the daemon scripts and
|
5
|
+
# handles the setup.
|
6
|
+
#
|
7
|
+
require 'listener_client'
|
8
|
+
require 'listener_main'
|
9
|
+
require 'mechanize_submitter'
|
10
|
+
require 'stomp_subscriber'
|
11
|
+
require 'submitter'
|
12
|
+
require 'subscriber'
|
13
|
+
|
14
|
+
module RMB
|
15
|
+
# This multi-level hash contains the initial set of properties needed to launch a Listener daemon.
|
16
|
+
#* Make a writable copy of this hash
|
17
|
+
#* Fill in the areas denoted by <<fill>> with your values
|
18
|
+
#* Add additional properties required by the Subscriber and Submitter classes used
|
19
|
+
#* Pass this has as the sole parameter in listener_client = ListenerClient.new(hash) to set up the daemon
|
20
|
+
#* Call listener_client.start to start the daemon.
|
21
|
+
RMB_Properties = {
|
22
|
+
:key => "<<fill>>",
|
23
|
+
:subscriber => {
|
24
|
+
:class_name => "<<fill>>" #set this to selected subclass of Subscriber
|
25
|
+
# <== add more properties for your Subscriber here
|
26
|
+
},
|
27
|
+
:submitter => {
|
28
|
+
:class_name => "<<fill>>" #set this to selected subclass of Submitter
|
29
|
+
# <== add more properties for your Submitter here
|
30
|
+
},
|
31
|
+
:working_dir => "<<fill>>", #set this to the RAILS_ROOT directory
|
32
|
+
|
33
|
+
:daemon_options => { #these options are passed directly to the class method Daemons.run(target, options)
|
34
|
+
:app_name => "", #custom name for this daemon, set by ListenerClient#initialize
|
35
|
+
:ARGV => nil, #use the program defaults
|
36
|
+
:dir_mode => :normal, #requires absolute path
|
37
|
+
:dir => "<<fill>>", #this is set to "#{working_dir}/tmp/pids" by ListenerClient#setup
|
38
|
+
:multiple => false, #this will allow multiple daemons to run
|
39
|
+
:ontop => false, #
|
40
|
+
:mode => :load,
|
41
|
+
:backtrace => false,
|
42
|
+
:monitor => false,
|
43
|
+
:log_output => true,
|
44
|
+
:keep_pid_files => true,
|
45
|
+
:hard_exit => true
|
46
|
+
}
|
47
|
+
}
|
48
|
+
|
49
|
+
LD = "listener_daemon_" #prefix for all listener_daemon artifacts produced: pids, log files, property files, and message files
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
if __FILE__ == $0
|
54
|
+
hash = RMB::ListenerClient.properties('inventory', RAILS_ROOT, 'StompSubscriber', 'MechanizeSubmitter')
|
55
|
+
hash[:daemon_options][:dir] = File.join("#{hash[:working_dir]}", "tmp", "pids")
|
56
|
+
front = hash[:subscriber]
|
57
|
+
front[:url] = '/topic/inventory'
|
58
|
+
front[:host] = 'localhost'
|
59
|
+
front[:port] = 61613
|
60
|
+
front[:user] = nil
|
61
|
+
front[:password] = nil
|
62
|
+
|
63
|
+
back = hash[:submitter]
|
64
|
+
back[:login_url] = 'http://localhost:3000/login'
|
65
|
+
back[:delivery_url] = 'http://localhost:3000/documents/new'
|
66
|
+
lc = RMB::ListenerClient.new(hash)
|
67
|
+
puts lc.inspect
|
68
|
+
lc.start
|
69
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'stomp'
|
2
|
+
require 'subscriber'
|
3
|
+
|
4
|
+
module RMB
|
5
|
+
|
6
|
+
class StompSubscriber < Subscriber
|
7
|
+
attr_accessor :url, :host, :port, :user, :password, :connection, :logger
|
8
|
+
=begin rdoc
|
9
|
+
+connect+ Opens a connection with the message broker, and then subscribes on the url
|
10
|
+
=end
|
11
|
+
def connect
|
12
|
+
super
|
13
|
+
begin
|
14
|
+
@connection = Stomp::Connection.open(user, password, host, port)
|
15
|
+
@connection.subscribe url, { :ack => 'auto' }
|
16
|
+
rescue Exception
|
17
|
+
logger.fatal "#{__FILE__}:#{__LINE__} Exception #{$!}"
|
18
|
+
raise
|
19
|
+
end
|
20
|
+
logger.info "Waiting for messages in #{url}."
|
21
|
+
end
|
22
|
+
=begin rdoc
|
23
|
+
+properties=(hash)+ Accepts a hash object containing all of the configuration properties required. These properties are copied into instance variables.
|
24
|
+
=end
|
25
|
+
def properties=(hash)
|
26
|
+
subscriber = hash[:subscriber]
|
27
|
+
@url = subscriber[:url] || "/queue/something"
|
28
|
+
@host = subscriber[:host] || ""
|
29
|
+
@port = subscriber[:port] || 61613
|
30
|
+
@user = subscriber[:user] || ""
|
31
|
+
@password = subscriber[:password] || ""
|
32
|
+
@connection = nil
|
33
|
+
super
|
34
|
+
end
|
35
|
+
=begin rdoc
|
36
|
+
+receive+ Executes a receive on the connection, thus blocking the daemon until a message arrives. Then answers the message.
|
37
|
+
=end
|
38
|
+
def receive
|
39
|
+
super
|
40
|
+
begin
|
41
|
+
message = @connection.receive
|
42
|
+
rescue Exception
|
43
|
+
logger.fatal "#{__FILE__}:#{__LINE__} Exception #{$!}"
|
44
|
+
raise
|
45
|
+
end
|
46
|
+
logger.info "Received message: #{message.inspect}"
|
47
|
+
message
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
data/lib/submitter.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
module RMB
|
2
|
+
|
3
|
+
class Submitter
|
4
|
+
=begin rdoc
|
5
|
+
+properties=(hash)+ This method establishes the logger for the subclass.
|
6
|
+
=end
|
7
|
+
def properties=(hash)
|
8
|
+
@logger = hash[:logger]
|
9
|
+
end
|
10
|
+
=begin rdoc
|
11
|
+
+connect+ This is an abstract method, intended to be implemented by a subclass.
|
12
|
+
=end
|
13
|
+
def connect
|
14
|
+
end
|
15
|
+
=begin rdoc
|
16
|
+
+connect+ This is an abstract method, intended to be implemented by a subclass.
|
17
|
+
=end
|
18
|
+
def send(message)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
data/lib/subscriber.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
module RMB
|
2
|
+
|
3
|
+
class Subscriber
|
4
|
+
=begin rdoc
|
5
|
+
+connect+ This is an abstract method, intended to be implemented by a subclass.
|
6
|
+
=end
|
7
|
+
def connect
|
8
|
+
end
|
9
|
+
=begin rdoc
|
10
|
+
+receive+ This is an abstract method, intended to be implemented by a subclass.
|
11
|
+
=end
|
12
|
+
def receive
|
13
|
+
end
|
14
|
+
=begin rdoc
|
15
|
+
+properties=(hash)+ This method establishes the logger for the subclass.
|
16
|
+
=end
|
17
|
+
def properties=(hash)
|
18
|
+
@logger = hash[:logger]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
data/rmb-rails.gemspec
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{rmb-rails}
|
5
|
+
s.version = "0.0.0"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Ken Burgett"]
|
9
|
+
s.date = %q{2009-07-21}
|
10
|
+
s.email = %q{keburgett@gmail.com}
|
11
|
+
s.extra_rdoc_files = [
|
12
|
+
"LICENSE",
|
13
|
+
"README.rdoc"
|
14
|
+
]
|
15
|
+
s.files = [
|
16
|
+
".document",
|
17
|
+
".gitignore",
|
18
|
+
"LICENSE",
|
19
|
+
"README.rdoc",
|
20
|
+
"Rakefile",
|
21
|
+
"VERSION.yml",
|
22
|
+
"lib/listener_client.rb",
|
23
|
+
"lib/listener_daemon.rb",
|
24
|
+
"lib/listener_daemon_control.rb",
|
25
|
+
"lib/listener_main.rb",
|
26
|
+
"lib/mechanize_submitter.rb",
|
27
|
+
"lib/rmb-rails.rb",
|
28
|
+
"lib/rmb2.rb",
|
29
|
+
"lib/stomp_subscriber.rb",
|
30
|
+
"lib/submitter.rb",
|
31
|
+
"lib/subscriber.rb",
|
32
|
+
"rmb-rails.gemspec",
|
33
|
+
"test/rmb-rails_test.rb",
|
34
|
+
"test/test_helper.rb"
|
35
|
+
]
|
36
|
+
s.has_rdoc = true
|
37
|
+
s.homepage = %q{http://github.com/explainer/rmb-rails}
|
38
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
39
|
+
s.require_paths = ["lib"]
|
40
|
+
s.rubyforge_project = %q{rmb-rails}
|
41
|
+
s.rubygems_version = %q{1.3.1}
|
42
|
+
s.summary = %q{RESTful Message Beans for Rails}
|
43
|
+
s.test_files = [
|
44
|
+
"test/test_helper.rb",
|
45
|
+
"test/rmb-rails_test.rb"
|
46
|
+
]
|
47
|
+
|
48
|
+
if s.respond_to? :specification_version then
|
49
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
50
|
+
s.specification_version = 2
|
51
|
+
|
52
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
53
|
+
else
|
54
|
+
end
|
55
|
+
else
|
56
|
+
end
|
57
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: explainer-rmb-rails
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ken Burgett
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-07-21 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description:
|
17
|
+
email: keburgett@gmail.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- LICENSE
|
24
|
+
- README.rdoc
|
25
|
+
files:
|
26
|
+
- .document
|
27
|
+
- .gitignore
|
28
|
+
- LICENSE
|
29
|
+
- README.rdoc
|
30
|
+
- Rakefile
|
31
|
+
- VERSION.yml
|
32
|
+
- lib/listener_client.rb
|
33
|
+
- lib/listener_daemon.rb
|
34
|
+
- lib/listener_daemon_control.rb
|
35
|
+
- lib/listener_main.rb
|
36
|
+
- lib/mechanize_submitter.rb
|
37
|
+
- lib/rmb-rails.rb
|
38
|
+
- lib/rmb2.rb
|
39
|
+
- lib/stomp_subscriber.rb
|
40
|
+
- lib/submitter.rb
|
41
|
+
- lib/subscriber.rb
|
42
|
+
- rmb-rails.gemspec
|
43
|
+
- test/rmb-rails_test.rb
|
44
|
+
- test/test_helper.rb
|
45
|
+
has_rdoc: true
|
46
|
+
homepage: http://github.com/explainer/rmb-rails
|
47
|
+
post_install_message:
|
48
|
+
rdoc_options:
|
49
|
+
- --charset=UTF-8
|
50
|
+
require_paths:
|
51
|
+
- lib
|
52
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: "0"
|
57
|
+
version:
|
58
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: "0"
|
63
|
+
version:
|
64
|
+
requirements: []
|
65
|
+
|
66
|
+
rubyforge_project: rmb-rails
|
67
|
+
rubygems_version: 1.2.0
|
68
|
+
signing_key:
|
69
|
+
specification_version: 2
|
70
|
+
summary: RESTful Message Beans for Rails
|
71
|
+
test_files:
|
72
|
+
- test/test_helper.rb
|
73
|
+
- test/rmb-rails_test.rb
|