reliable-msg 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +20 -0
- data/README +123 -0
- data/Rakefile +164 -0
- data/bin/queues +8 -0
- data/lib/reliable-msg.rb +12 -0
- data/lib/reliable-msg/cli.rb +170 -0
- data/lib/reliable-msg/message-store.rb +509 -0
- data/lib/reliable-msg/mysql.sql +8 -0
- data/lib/reliable-msg/queue-manager.rb +409 -0
- data/lib/reliable-msg/queue.rb +500 -0
- data/lib/reliable-msg/selector.rb +109 -0
- data/lib/uuid.rb +384 -0
- data/test/test-queue.rb +144 -0
- data/test/test-uuid.rb +48 -0
- metadata +63 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2005 Assaf Arkin
|
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
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
= Reliable Messaging for Ruby
|
2
|
+
|
3
|
+
This package provides reliable messaging and persistent queues for
|
4
|
+
building asynchronous applications in Ruby.
|
5
|
+
|
6
|
+
This release provides the following features:
|
7
|
+
|
8
|
+
* Simple API.
|
9
|
+
|
10
|
+
* Transction processing.
|
11
|
+
|
12
|
+
* Disk-based and MySQL message stores.
|
13
|
+
|
14
|
+
* Best effort, repeated and once-only delivery semantics.
|
15
|
+
|
16
|
+
* Priority queues, message expiration, dead-letter queue.
|
17
|
+
|
18
|
+
* Message selectors.
|
19
|
+
|
20
|
+
* Local and remote queue managers using DRb.
|
21
|
+
|
22
|
+
|
23
|
+
== Download
|
24
|
+
|
25
|
+
The latest version of Reliable Messaging can be found at
|
26
|
+
|
27
|
+
* http://rubyforge.org/projects/reliable-msg/
|
28
|
+
|
29
|
+
For more information (wiki, bug reports)
|
30
|
+
|
31
|
+
* http://trac.labnotes.org/cgi-bin/trac.cgi/wiki/RubyReliableMessaging
|
32
|
+
|
33
|
+
|
34
|
+
== Installation
|
35
|
+
|
36
|
+
You can download the sources directly, or install the GEM package
|
37
|
+
(recommended) with
|
38
|
+
|
39
|
+
gem install --remote reliable-msg
|
40
|
+
|
41
|
+
To create the configuration file and queues for use with the disk-based
|
42
|
+
message store
|
43
|
+
|
44
|
+
queues install disk [<path>]
|
45
|
+
|
46
|
+
The optional <tt>path</tt> argument specifies the directory in which
|
47
|
+
queue index and messages are store. The default directory is
|
48
|
+
<tt>queues</tt>.
|
49
|
+
|
50
|
+
To create the configuration file and queues for use with the MySQL
|
51
|
+
message store
|
52
|
+
|
53
|
+
queues install mysql <host> <user> <password> <database>
|
54
|
+
[--port <port>] [--socket <socket>] [--prefix <prefix>]
|
55
|
+
|
56
|
+
You must have MySQL libraries installed in order to use this message
|
57
|
+
store, either native MySQL libraries or the Rails pure-Ruby adapter.
|
58
|
+
It uses the supplied connection properties and creates tables in your
|
59
|
+
database using the specified prefix. The default prefix is
|
60
|
+
<tt>reliable_msg_</tt>.
|
61
|
+
|
62
|
+
For example
|
63
|
+
|
64
|
+
queues install mysql localhost my-db user secret
|
65
|
+
|
66
|
+
=== Configuration file
|
67
|
+
|
68
|
+
The installation process creates a configuration file
|
69
|
+
(<tt>queues.cfg</tt>) located in the same directory as the library.
|
70
|
+
You can specify a different location for the configuration file using
|
71
|
+
the option <tt>-c</tt> of <tt>--config</tt>.
|
72
|
+
|
73
|
+
When you use the queue manager, it looks for a specified configuration
|
74
|
+
file, or if no configuration file is specified it looks for the
|
75
|
+
<tt>queues.cfg</tt> file, first in the local directory and then in the
|
76
|
+
installation directory. If no file exists, it will create a default one
|
77
|
+
to use the disk-based message store.
|
78
|
+
|
79
|
+
=== UUID state file
|
80
|
+
|
81
|
+
Reliable messaging requires a UUID generator that is able to create
|
82
|
+
universally unique identifiers. The UUID generator uses the
|
83
|
+
<tt>uuid.state</tt> file to hold a unique machine identifier and a
|
84
|
+
rolling sequence number.
|
85
|
+
|
86
|
+
On the first usage, a <tt>uuid.state</tt> file is created in the
|
87
|
+
installation directory. An existing state file will be used if one
|
88
|
+
exists in the local directory or the installation directory.
|
89
|
+
|
90
|
+
The unique machine identifier is obtained from one of the network
|
91
|
+
card's MAC address on your machine, using either the +ipconfig+ or
|
92
|
+
+ifconfig+ commands. If no MAC address can be found, or you want to
|
93
|
+
pick a specific MAC address, create a <tt>uuid.state</tt> file
|
94
|
+
manually.
|
95
|
+
|
96
|
+
|
97
|
+
== Simple Example
|
98
|
+
|
99
|
+
Start the queue manager as a standalone server
|
100
|
+
|
101
|
+
queues manager start
|
102
|
+
|
103
|
+
Use the queue API in your application
|
104
|
+
|
105
|
+
require 'reliable-msg'
|
106
|
+
|
107
|
+
queue = Queue.new 'my-queue'
|
108
|
+
queue.put obj
|
109
|
+
msg = queue.get
|
110
|
+
assert msg.object == obj
|
111
|
+
|
112
|
+
Stop the queue manager
|
113
|
+
|
114
|
+
queues manager stop
|
115
|
+
|
116
|
+
|
117
|
+
== License
|
118
|
+
|
119
|
+
This package is licensed under the MIT license and/or the {Creative
|
120
|
+
Commons Attribution-ShareAlike}[http://creativecommons.org/licenses/by-sa/2.5/legalcode].
|
121
|
+
|
122
|
+
:include: MIT-LICENSE
|
123
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,164 @@
|
|
1
|
+
# Adapted from the rake Rakefile.
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
Gem::manage_gems
|
5
|
+
require 'rake/testtask'
|
6
|
+
require 'rake/rdoctask'
|
7
|
+
require 'rake/gempackagetask'
|
8
|
+
|
9
|
+
|
10
|
+
desc "Default Task"
|
11
|
+
task :default => [:tests, :rdoc]
|
12
|
+
|
13
|
+
|
14
|
+
desc "Run test case for UUID generator"
|
15
|
+
Rake::TestTask.new :test_uuid do |test|
|
16
|
+
test.verbose = true
|
17
|
+
test.test_files = ['test/test-uuid.rb']
|
18
|
+
end
|
19
|
+
desc "Run test case for Queue API generator"
|
20
|
+
Rake::TestTask.new :test_queue do |test|
|
21
|
+
test.verbose = true
|
22
|
+
test.test_files = ['test/test-queue.rb']
|
23
|
+
end
|
24
|
+
desc "Run all test cases"
|
25
|
+
Rake::TestTask.new :tests=>[:test_uuid, :test_queue] do |test|
|
26
|
+
test.verbose = true
|
27
|
+
test.test_files = []
|
28
|
+
#test.warning = true
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
# Create the documentation.
|
33
|
+
Rake::RDocTask.new do |rdoc|
|
34
|
+
rdoc.main = "README"
|
35
|
+
rdoc.rdoc_files.include("README", "lib/**/*.rb")
|
36
|
+
rdoc.title = 'Reliable Messaging'
|
37
|
+
end
|
38
|
+
|
39
|
+
# Handle version number.
|
40
|
+
class Version
|
41
|
+
|
42
|
+
PATTERN = /(\s*)VERSION.*(\d+\.\d+\.\d+)/
|
43
|
+
|
44
|
+
def initialize file, new_version
|
45
|
+
@file = file
|
46
|
+
@version = File.open @file, 'r' do |file|
|
47
|
+
version = nil
|
48
|
+
file.each_line do |line|
|
49
|
+
match = line.match PATTERN
|
50
|
+
if match
|
51
|
+
version = match[2]
|
52
|
+
break
|
53
|
+
end
|
54
|
+
end
|
55
|
+
version
|
56
|
+
end
|
57
|
+
fail "Can't determine version number" unless @version
|
58
|
+
@new_version = new_version || @version
|
59
|
+
end
|
60
|
+
|
61
|
+
def changed?
|
62
|
+
@version != @new_version
|
63
|
+
end
|
64
|
+
|
65
|
+
def number
|
66
|
+
@version
|
67
|
+
end
|
68
|
+
|
69
|
+
def next
|
70
|
+
@new_version
|
71
|
+
end
|
72
|
+
|
73
|
+
def update
|
74
|
+
puts "Updating to version #{@new_version}"
|
75
|
+
copy = "#{@file}.new"
|
76
|
+
open @file, "r" do |input|
|
77
|
+
open copy, "w" do |output|
|
78
|
+
input.each_line do |line|
|
79
|
+
match = line.match PATTERN
|
80
|
+
if match
|
81
|
+
output.puts "#{match[1]}VERSION = '#{@new_version}'"
|
82
|
+
else
|
83
|
+
output.puts line
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
mv copy, @file
|
89
|
+
@version = @new_version
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
version = Version.new 'lib/reliable-msg.rb', ENV['version']
|
94
|
+
|
95
|
+
|
96
|
+
# Create the GEM package.
|
97
|
+
gem_spec = Gem::Specification.new do |spec|
|
98
|
+
spec.name = 'reliable-msg'
|
99
|
+
spec.version = version.next
|
100
|
+
spec.summary = "Reliable messaging and persistent queues for building asynchronous applications in Ruby"
|
101
|
+
spec.description = <<-EOF
|
102
|
+
This package provides reliable messaging and persistent queues for
|
103
|
+
building asynchronous applications in Ruby.
|
104
|
+
|
105
|
+
It supports transaction processing, message selectors, priorities,
|
106
|
+
delivery semantics, remote queue managers, disk-based and MySQL message
|
107
|
+
stores and more.
|
108
|
+
EOF
|
109
|
+
spec.author = "Assaf Arkin"
|
110
|
+
spec.email = "assaf@labnotes.org"
|
111
|
+
spec.homepage = "http://trac.labnotes.org/cgi-bin/trac.cgi/wiki/RubyReliableMessaging"
|
112
|
+
|
113
|
+
spec.files = FileList["{bin,test,lib,docs}/**/*", "README", "MIT-LICENSE", "Rakefile"].to_a
|
114
|
+
spec.require_path = "lib"
|
115
|
+
spec.autorequire = 'reliable-msg.rb'
|
116
|
+
spec.bindir = "bin"
|
117
|
+
spec.executables = ["queues"]
|
118
|
+
spec.default_executable = "queues"
|
119
|
+
spec.requirements << "MySQL for database store, otherwise uses the file system"
|
120
|
+
spec.has_rdoc = true
|
121
|
+
spec.rdoc_options << '--main' << 'README' << '--title' << 'Reliable Messaging for Ruby' << '--line-numbers'
|
122
|
+
spec.extra_rdoc_files = ["README"]
|
123
|
+
end
|
124
|
+
|
125
|
+
gem = Rake::GemPackageTask.new(gem_spec) do |pkg|
|
126
|
+
pkg.need_tar_gz = true
|
127
|
+
pkg.need_zip = true
|
128
|
+
end
|
129
|
+
|
130
|
+
|
131
|
+
desc "Look for TODO and FIXME tags in the code"
|
132
|
+
task :todo do
|
133
|
+
FileList['**/*.rb'].egrep /#.*(FIXME|TODO|TBD)/
|
134
|
+
end
|
135
|
+
|
136
|
+
|
137
|
+
# --------------------------------------------------------------------
|
138
|
+
# Creating a release
|
139
|
+
|
140
|
+
desc "Make a new release"
|
141
|
+
task :release => [:tests, :prerelease, :clobber, :update_version, :package] do
|
142
|
+
puts
|
143
|
+
puts "**************************************************************"
|
144
|
+
puts "* Release #{version.number} Complete."
|
145
|
+
puts "* Packages ready to upload."
|
146
|
+
puts "**************************************************************"
|
147
|
+
puts
|
148
|
+
end
|
149
|
+
|
150
|
+
task :prerelease do
|
151
|
+
if !version.changed? && ENV['reuse'] != version.number
|
152
|
+
fail "Current version is #{version.number}, must specify reuse=ver to reuse existing version"
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
task :update_version => [:prerelease] do
|
157
|
+
if !version.changed?
|
158
|
+
puts "No version change ... skipping version update"
|
159
|
+
else
|
160
|
+
version.update
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
|
data/bin/queues
ADDED
data/lib/reliable-msg.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__)) unless
|
2
|
+
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
3
|
+
|
4
|
+
module ReliableMsg
|
5
|
+
|
6
|
+
PACKAGE = "reliable-msg"
|
7
|
+
VERSION = '1.0.0'
|
8
|
+
|
9
|
+
end
|
10
|
+
|
11
|
+
require 'reliable-msg/queue'
|
12
|
+
require 'reliable-msg/cli'
|
@@ -0,0 +1,170 @@
|
|
1
|
+
#
|
2
|
+
# = cli.rb - Reliable messaging command-line interface
|
3
|
+
#
|
4
|
+
# Author:: Assaf Arkin assaf@labnotes.org
|
5
|
+
# Documentation:: http://trac.labnotes.org/cgi-bin/trac.cgi/wiki/RubyReliableMessaging
|
6
|
+
# Copyright:: Copyright (c) 2005 Assaf Arkin
|
7
|
+
# License:: MIT and/or Creative Commons Attribution-ShareAlike
|
8
|
+
#
|
9
|
+
#--
|
10
|
+
# Changes:
|
11
|
+
#++
|
12
|
+
|
13
|
+
|
14
|
+
require 'drb'
|
15
|
+
require 'optparse'
|
16
|
+
require 'rdoc/usage'
|
17
|
+
require 'reliable-msg/queue-manager'
|
18
|
+
|
19
|
+
module ReliableMsg
|
20
|
+
|
21
|
+
class CLI #:nodoc:
|
22
|
+
|
23
|
+
USAGE = <<-EOF
|
24
|
+
usage: queues [-c config] command [args]
|
25
|
+
|
26
|
+
To see list of available commands and options
|
27
|
+
queues help
|
28
|
+
EOF
|
29
|
+
|
30
|
+
HELP = <<-EOF
|
31
|
+
usage: queues [-c config] command [args]
|
32
|
+
|
33
|
+
Reliable messaging queue manager, version #{VERSION}
|
34
|
+
|
35
|
+
Available commands:
|
36
|
+
|
37
|
+
help
|
38
|
+
Display this help message.
|
39
|
+
|
40
|
+
manager start
|
41
|
+
Start the queue manager as a standalone server
|
42
|
+
|
43
|
+
manager stop
|
44
|
+
Stop a running queue manager.
|
45
|
+
|
46
|
+
install disk [<path>]
|
47
|
+
Configure queue manager to use disk-based message store
|
48
|
+
using the specified directory. Uses 'queues' by default.
|
49
|
+
|
50
|
+
install mysql <host> <username> <password> <database> [options]
|
51
|
+
[--port <port>] [--socket <socket>] [--prefix <prefix>]
|
52
|
+
Configure queue manager to use MySQL for message store,
|
53
|
+
using the specified connection properties. Updates database
|
54
|
+
schema.
|
55
|
+
|
56
|
+
Options for install mysql are (defaults apply if missing):
|
57
|
+
|
58
|
+
--port Port to connect to
|
59
|
+
--socket Socket to connect to
|
60
|
+
--prefix Prefix for table names
|
61
|
+
|
62
|
+
EOF
|
63
|
+
|
64
|
+
class InvalidUsage < Exception
|
65
|
+
end
|
66
|
+
|
67
|
+
def initialize
|
68
|
+
end
|
69
|
+
|
70
|
+
def run
|
71
|
+
begin
|
72
|
+
config_file = nil
|
73
|
+
opts = OptionParser.new
|
74
|
+
opts.on("-c FILE", "--config FILE", String) { |value| config_file = value }
|
75
|
+
opts.on("-v", "--version") do
|
76
|
+
puts "Reliable messaging queue manager, version #{VERSION}"
|
77
|
+
exit
|
78
|
+
end
|
79
|
+
opts.on("-h", "--help") do
|
80
|
+
puts HELP
|
81
|
+
exit
|
82
|
+
end
|
83
|
+
|
84
|
+
args = opts.parse(ARGV)
|
85
|
+
|
86
|
+
raise InvalidUsage if args.length < 1
|
87
|
+
case args[0]
|
88
|
+
when 'help'
|
89
|
+
puts HELP
|
90
|
+
|
91
|
+
when 'manager'
|
92
|
+
case args[1]
|
93
|
+
when 'start', nil
|
94
|
+
manager = QueueManager.new({:config=>config_file})
|
95
|
+
manager.start
|
96
|
+
begin
|
97
|
+
while manager.alive?
|
98
|
+
sleep 3
|
99
|
+
end
|
100
|
+
rescue Interrupt
|
101
|
+
manager.stop
|
102
|
+
end
|
103
|
+
when 'stop'
|
104
|
+
if config_file
|
105
|
+
config = Config.new config_file, nil
|
106
|
+
unless config.load_no_create || config_file.nil?
|
107
|
+
puts "Could not find configuration file #{config.path}"
|
108
|
+
exit
|
109
|
+
end
|
110
|
+
drb = Config::DEFAULT_DRB
|
111
|
+
drb.merge(config.drb) if config.drb
|
112
|
+
drb_uri = "druby://localhost:#{drb['port']}"
|
113
|
+
else
|
114
|
+
drb_uri = Queue::DEFAULT_DRB
|
115
|
+
end
|
116
|
+
begin
|
117
|
+
DRbObject.new(nil, drb_uri).stop
|
118
|
+
rescue DRb::DRbConnError =>error
|
119
|
+
puts "No queue manager at #{drb_uri}"
|
120
|
+
end
|
121
|
+
else
|
122
|
+
raise InvalidUsage
|
123
|
+
end
|
124
|
+
|
125
|
+
when 'install'
|
126
|
+
config = Config.new config_file, nil
|
127
|
+
case args[1]
|
128
|
+
when 'disk'
|
129
|
+
store = MessageStore::Disk.new({}, nil)
|
130
|
+
config.store = store.configuration
|
131
|
+
if config.create_if_none
|
132
|
+
store.setup
|
133
|
+
puts "Created queues configuration file: #{config.path}"
|
134
|
+
else
|
135
|
+
puts "Found existing queues configuration file: #{config.path}"
|
136
|
+
puts "No changes made"
|
137
|
+
end
|
138
|
+
when 'mysql'
|
139
|
+
host, username, password, database = args[2], args[3], args[4], args[5]
|
140
|
+
raise InvalidUsage unless host && database && username && password
|
141
|
+
conn = { "host"=>host, "username"=>username, "password"=>password, "database"=>database }
|
142
|
+
store = MessageStore::MySQL.new(conn, nil)
|
143
|
+
config.store = store.configuration
|
144
|
+
if config.create_if_none
|
145
|
+
puts "Created queues configuration file: #{config.path}"
|
146
|
+
if store.setup
|
147
|
+
puts "Created queue manager tables in database '#{database}'"
|
148
|
+
end
|
149
|
+
else
|
150
|
+
puts "Found existing queues configuration file: #{config.path}"
|
151
|
+
puts "No changes made"
|
152
|
+
end
|
153
|
+
else
|
154
|
+
raise InvalidUsage
|
155
|
+
end
|
156
|
+
else
|
157
|
+
raise InvalidUsage
|
158
|
+
end
|
159
|
+
rescue InvalidUsage
|
160
|
+
puts USAGE
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
end
|
165
|
+
|
166
|
+
end
|
167
|
+
|
168
|
+
if __FILE__ == $0
|
169
|
+
ReliableMsg::CLI.new.run
|
170
|
+
end
|