fmq 0.3.0 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +11 -1
- data/README.txt +1 -1
- data/default-server/config.ru +14 -16
- data/default-server/queues/my_test.rb +3 -3
- data/lib/fmq/admin.rb +64 -51
- data/lib/fmq/queue_manager.rb +56 -136
- data/lib/fmq/queues/README.txt +3 -3
- data/lib/fmq/queues/base.rb +72 -8
- data/lib/fmq/queues/linked.rb +4 -4
- data/lib/fmq/queues/load_balanced.rb +10 -0
- data/lib/fmq/server.rb +17 -17
- data/lib/fmq/version.rb +1 -1
- data/test/test_basic.rb +56 -6
- data/test/test_queue_manager.rb +18 -14
- metadata +2 -2
data/History.txt
CHANGED
@@ -1,4 +1,14 @@
|
|
1
|
-
== 0.3.
|
1
|
+
== 0.3.1 2008-08-27
|
2
|
+
|
3
|
+
* 2 minor changes:
|
4
|
+
* refactored the queue manager and remove legacy code (moved constraints stuff to the base queue)
|
5
|
+
* refactored the setup_queue method in the configuration file (sorry you have to change your configs)
|
6
|
+
* 3 tiny changes:
|
7
|
+
* default-server can now be used from within the development repository (no rund trip nessesary)
|
8
|
+
* added and replaced some tests for the new interface of the queue manager and the base queue
|
9
|
+
* one can now yield and return messages
|
10
|
+
|
11
|
+
== 0.3.0 2008-08-26
|
2
12
|
|
3
13
|
* 2 minor changes:
|
4
14
|
* replaced the mongrel handler by rack so that it can be used by many servers and in different configurations
|
data/README.txt
CHANGED
@@ -34,7 +34,7 @@ stores it’s internal data in an FIFO in system memory.
|
|
34
34
|
== FEATURES/PROBLEMS:
|
35
35
|
|
36
36
|
* FIFO message store
|
37
|
-
* easy setup and maintenance of system
|
37
|
+
* easy setup and maintenance of system (rack)
|
38
38
|
* using http for communication
|
39
39
|
* changeable queue implementation
|
40
40
|
* ruby client lib
|
data/default-server/config.ru
CHANGED
@@ -1,4 +1,8 @@
|
|
1
|
-
|
1
|
+
begin
|
2
|
+
require File.dirname(__FILE__) + '/../lib/fmq'
|
3
|
+
rescue LoadError
|
4
|
+
require "fmq"
|
5
|
+
end
|
2
6
|
|
3
7
|
# load all local queues (from project directory)
|
4
8
|
Dir.glob("queues/*.rb").each { |f| require f }
|
@@ -24,39 +28,33 @@ queue_manager.setup do |qm|
|
|
24
28
|
# define there url and constraints here
|
25
29
|
# =====================================================
|
26
30
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
q.path = "/fmq_test/test1"
|
31
|
+
# the path to the queue e.g. /app1/myframe/test1
|
32
|
+
# means http://localhost:5884/app1/myframe/test1
|
33
|
+
# this parameter is not optional
|
34
|
+
qm.setup_queue "/fmq_test/test1" do |q|
|
32
35
|
# this defines the maximum count of messages that
|
33
36
|
# can be in the queue, if the queue is full every
|
34
37
|
# new message will be rejected with a http error
|
35
38
|
# this parameter is optional if you don't specify
|
36
39
|
# a max value the queue size depends on your system
|
37
|
-
q.max_messages =
|
40
|
+
q.max_messages = 1_000_000
|
38
41
|
# this optional to and specifys the max content size
|
39
42
|
# for all data of a queue
|
40
43
|
# valid extensions are kb, mb, gb
|
41
|
-
q.max_size =
|
44
|
+
q.max_size = 10.kb
|
42
45
|
end
|
43
46
|
|
44
47
|
# if you want you can specify the class of the queue
|
45
48
|
# this is interessting if you write your own queues
|
46
|
-
qm.setup_queue FreeMessageQueue::LoadBalancedQueue
|
47
|
-
q.path = "/fmq_test/test2"
|
48
|
-
end
|
49
|
+
qm.setup_queue "/fmq_test/test2", FreeMessageQueue::LoadBalancedQueue
|
49
50
|
|
50
51
|
# if you have special queues include put them into the queues
|
51
52
|
# folder and and use them (this MyTestQueue is places in queues/mytest.rb)
|
52
|
-
qm.setup_queue MyTestQueue
|
53
|
-
q.path = "/fmq_test/test3"
|
54
|
-
end
|
53
|
+
qm.setup_queue "/fmq_test/test3", MyTestQueue
|
55
54
|
|
56
55
|
# this is a forwarding queue wich forwards one message
|
57
56
|
# to some other queues
|
58
|
-
qm.setup_queue FreeMessageQueue::ForwardQueue do |q|
|
59
|
-
q.path = "/fmq_test/forward_to_1_and_2"
|
57
|
+
qm.setup_queue "/fmq_test/forward_to_1_and_2", FreeMessageQueue::ForwardQueue do |q|
|
60
58
|
# you can add as may queues as you want
|
61
59
|
# but seperate them with a space char
|
62
60
|
q.forward_to = ["/fmq_test/test1", "/fmq_test/test2"]
|
@@ -4,8 +4,8 @@ class MyTestQueue < FreeMessageQueue::BaseQueue
|
|
4
4
|
end
|
5
5
|
|
6
6
|
def poll
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
FreeMessageQueue::Message.new "Hello World", "text/plain" do |m|
|
8
|
+
m.option["Time"] = Time.now
|
9
|
+
end
|
10
10
|
end
|
11
11
|
end
|
data/lib/fmq/admin.rb
CHANGED
@@ -25,72 +25,85 @@ end
|
|
25
25
|
|
26
26
|
module FreeMessageQueue
|
27
27
|
# This class is dedicated to the AJAX based admin interface.
|
28
|
-
class AdminInterface
|
29
|
-
# rack response for bad requests
|
30
|
-
WRONG_METHOD = [400, {"CONTENT-TYPE" => "text/plain", "ERROR" => "Wrong http Method"}, ""]
|
31
|
-
|
32
|
-
# rack response for good requests
|
33
|
-
OK = [200, {"CONTENT-TYPE" => "text/plain"}, ["ok"]]
|
34
|
-
|
35
|
-
# create the admin interface for the passed QueueManager
|
36
|
-
def initialize(queue_manager)
|
37
|
-
@manager = queue_manager
|
38
|
-
@log = FreeMessageQueue.logger
|
39
|
-
end
|
40
|
-
|
41
|
-
# handle all requests
|
42
|
-
def call(env)
|
43
|
-
begin
|
44
|
-
request = Rack::Request.new(env)
|
45
|
-
@log.debug "[AdminInterface] ENV: #{env.inspect}"
|
46
|
-
|
47
|
-
if request.get? then
|
48
|
-
@log.info "[AdminInterface] list queues"
|
28
|
+
class AdminInterface
|
29
|
+
# rack response for bad requests
|
30
|
+
WRONG_METHOD = [400, {"CONTENT-TYPE" => "text/plain", "ERROR" => "Wrong http Method"}, ""]
|
31
|
+
|
32
|
+
# rack response for good requests
|
33
|
+
OK = [200, {"CONTENT-TYPE" => "text/plain"}, ["ok"]]
|
34
|
+
|
35
|
+
# create the admin interface for the passed QueueManager
|
36
|
+
def initialize(queue_manager)
|
37
|
+
@manager = queue_manager
|
38
|
+
@log = FreeMessageQueue.logger
|
39
|
+
end
|
40
|
+
|
41
|
+
# handle all requests
|
42
|
+
def call(env)
|
43
|
+
begin
|
44
|
+
request = Rack::Request.new(env)
|
45
|
+
@log.debug "[AdminInterface] ENV: #{env.inspect}"
|
46
|
+
|
47
|
+
if request.get? then
|
48
|
+
@log.info "[AdminInterface] list queues"
|
49
49
|
# ======= LIST QUEUES
|
50
50
|
queues_code = []
|
51
51
|
@manager.queues.each do |queue_name|
|
52
52
|
queues_code << queue_to_json(queue_name)
|
53
|
-
end
|
54
|
-
|
55
|
-
return [200, {"CONTENT-TYPE" => "application/json"}, ["[%s]" % queues_code.join(","), ]]
|
56
|
-
elsif request.post? then
|
57
|
-
if request["_method"] == "delete" then
|
58
|
-
# ======= DELETE QUEUE
|
53
|
+
end
|
54
|
+
|
55
|
+
return [200, {"CONTENT-TYPE" => "application/json"}, ["[%s]" % queues_code.join(","), ]]
|
56
|
+
elsif request.post? then
|
57
|
+
if request["_method"] == "delete" then
|
58
|
+
# ======= DELETE QUEUE
|
59
59
|
@log.info "[AdminInterface] delete queue"
|
60
|
-
@manager.delete_queue(request["path"])
|
61
|
-
return OK
|
62
|
-
elsif request["_method"] == "create"
|
63
|
-
# ======= CREATE QUEUE
|
60
|
+
@manager.delete_queue(request["path"])
|
61
|
+
return OK
|
62
|
+
elsif request["_method"] == "create"
|
63
|
+
# ======= CREATE QUEUE
|
64
64
|
@log.info "[AdminInterface] create queue"
|
65
|
-
@manager.
|
66
|
-
request["max_messages"].
|
67
|
-
request["max_size"]
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
65
|
+
@manager.setup_queue request["path"] do |q|
|
66
|
+
q.max_messages = request["max_messages"].to_i
|
67
|
+
q.max_size = str_bytes request["max_size"]
|
68
|
+
end
|
69
|
+
return OK
|
70
|
+
else
|
71
|
+
return WRONG_METHOD
|
72
|
+
end
|
73
|
+
else
|
74
|
+
return WRONG_METHOD
|
75
|
+
end
|
76
|
+
rescue QueueManagerException => ex
|
77
|
+
return [400, {"CONTENT-TYPE" => "text/plain", "ERROR" => ex.message}, [ex.message]]
|
78
|
+
end
|
78
79
|
end
|
79
80
|
|
80
81
|
private
|
81
82
|
|
82
83
|
# converts the data of one queue to json format
|
83
84
|
def queue_to_json(queue_name)
|
84
|
-
|
85
|
+
queue = @manager.queue(queue_name)
|
85
86
|
|
86
87
|
"[\"%s\", %d, %d, %d, %d]" % [
|
87
88
|
queue_name,
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
89
|
+
queue.bytes,
|
90
|
+
queue.max_size,
|
91
|
+
queue.size,
|
92
|
+
queue.max_messages,
|
92
93
|
]
|
93
94
|
end
|
95
|
+
|
96
|
+
# Retuns count of bytes to a expression with kb, mb or gb
|
97
|
+
# e.g 10kb will return 10240
|
98
|
+
def str_bytes(str)
|
99
|
+
case str
|
100
|
+
when /([0-9]+)kb/i: $1.to_i.kb
|
101
|
+
when /([0-9]+)mb/i: $1.to_i.mb
|
102
|
+
when /([0-9]+)gb/i: $1.to_i.gb
|
103
|
+
else
|
104
|
+
BaseQueue::INFINITE
|
105
|
+
end
|
106
|
+
end
|
94
107
|
end
|
95
|
-
end
|
96
|
-
|
108
|
+
end
|
109
|
+
|
data/lib/fmq/queue_manager.rb
CHANGED
@@ -18,6 +18,21 @@
|
|
18
18
|
#
|
19
19
|
require "ostruct"
|
20
20
|
|
21
|
+
# add kb, mb and gb methods for easy use in the config file
|
22
|
+
class Fixnum
|
23
|
+
def kb
|
24
|
+
self * 1024
|
25
|
+
end
|
26
|
+
|
27
|
+
def mb
|
28
|
+
self.kb * 1024
|
29
|
+
end
|
30
|
+
|
31
|
+
def gb
|
32
|
+
self.mb * 1024
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
21
36
|
module FreeMessageQueue
|
22
37
|
# All queue manager exceptions are raised using this class
|
23
38
|
class QueueManagerException < Exception
|
@@ -40,23 +55,8 @@ module FreeMessageQueue
|
|
40
55
|
# corresponding constraints. Every queue that is created by this
|
41
56
|
# queue manager will get a reference (<em>manager</em>) for later use.
|
42
57
|
class QueueManager
|
43
|
-
# Returns the queue that is passed otherwise nil
|
44
|
-
attr_reader :queue
|
45
|
-
|
46
58
|
# <b>true</b> to let the queue manager create a queue automaticly
|
47
59
|
attr_writer :auto_create_queues
|
48
|
-
|
49
|
-
# Returns the queue constrains as a hash. The hash has the following structure:
|
50
|
-
# {
|
51
|
-
# :max_size => "100mb",
|
52
|
-
# :max_messages => 1000
|
53
|
-
# }
|
54
|
-
attr_reader :queue_constraints
|
55
|
-
|
56
|
-
# This value is used to decribe that a constraint has no limit e.g.
|
57
|
-
# :max_messages => INFINITE
|
58
|
-
# means that there is no limitation for messages
|
59
|
-
INFINITE = -1
|
60
60
|
|
61
61
|
# this is the default queue class if no other is specified this
|
62
62
|
# class will be created when setting up a queue
|
@@ -65,8 +65,7 @@ module FreeMessageQueue
|
|
65
65
|
# setup the queue manager using the configuration from the configuration
|
66
66
|
# file (which is basically a hash)
|
67
67
|
def initialize()
|
68
|
-
@
|
69
|
-
@queue_constraints = {}
|
68
|
+
@queues = {}
|
70
69
|
@log = FreeMessageQueue.logger
|
71
70
|
@auto_create_queues = true
|
72
71
|
end
|
@@ -76,44 +75,26 @@ module FreeMessageQueue
|
|
76
75
|
def auto_create_queues?
|
77
76
|
@auto_create_queues == true
|
78
77
|
end
|
79
|
-
|
80
|
-
# Create a queue (<em>name</em> => <em>path</em>). The path must contain a leading "/" and a 3 character name
|
81
|
-
# at minimum. Exceptions will be raised if the queue allready exists.
|
82
|
-
def create_queue(name, max_messages = INFINITE, max_size = INFINITE, default_class = DEFAULT_QUEUE_CLASS)
|
83
|
-
# path must begin with /
|
84
|
-
raise QueueManagerException.new("[QueueManager] Leading / in path '#{name}' missing", caller) if name[0..0] != "/"
|
85
|
-
|
86
|
-
# path must have a minimus lenght of 3 character
|
87
|
-
raise QueueManagerException.new("[QueueManager] The queue path '#{name}' is to short 3 character is minimum", caller) if name.size - 1 < 3
|
88
|
-
|
89
|
-
# don't create a queue twice
|
90
|
-
raise QueueManagerException.new("[QueueManager] The queue '#{name}' allready exists", caller) if queue_exists? name
|
91
|
-
|
92
|
-
@log.info("[QueueManager] Create queue '#{name}' {type: #{default_class}, max_messages: #{max_messages}, max_size: #{max_size}}")
|
93
|
-
|
94
|
-
@queue[name] = default_class.new(self)
|
95
|
-
@queue_constraints[name] = {
|
96
|
-
:max_messages => max_messages,
|
97
|
-
:max_size => max_size
|
98
|
-
}
|
99
|
-
|
100
|
-
@queue[name]
|
101
|
-
end
|
102
78
|
|
103
79
|
# create a queue using a block. The block can be used to set configuration
|
104
80
|
# options for the queue
|
105
|
-
def setup_queue(queue_class = DEFAULT_QUEUE_CLASS
|
106
|
-
|
107
|
-
|
108
|
-
|
81
|
+
def setup_queue(path, queue_class = DEFAULT_QUEUE_CLASS)
|
82
|
+
check_queue_name(path)
|
83
|
+
queue_object = queue_class.new(self)
|
84
|
+
if block_given? then
|
85
|
+
yield queue_object
|
86
|
+
end
|
87
|
+
@queues[path] = queue_object
|
88
|
+
@log.info("[QueueManager] Create queue '#{path}' {type: #{queue_class}, max_messages: #{queue_object.max_messages}, max_size: #{queue_object.max_size}}")
|
89
|
+
return queue_object
|
109
90
|
end
|
110
91
|
|
111
92
|
# Delete the queue by name (path)
|
112
93
|
def delete_queue(name)
|
113
|
-
if
|
114
|
-
@log.info("[QueueManager] Delete queue '#{name}' with #{
|
115
|
-
|
116
|
-
@
|
94
|
+
if queue_exists? name
|
95
|
+
@log.info("[QueueManager] Delete queue '#{name}' with #{queue(name).size} messages")
|
96
|
+
queue(name).clear
|
97
|
+
@queues.delete name
|
117
98
|
true
|
118
99
|
else
|
119
100
|
raise QueueManagerException.new("[QueueManager] There is no queue '#{name}'", caller)
|
@@ -122,10 +103,10 @@ module FreeMessageQueue
|
|
122
103
|
|
123
104
|
# This returns one message from the passed queue
|
124
105
|
def poll(name)
|
125
|
-
if
|
126
|
-
@log.debug("[QueueManager] Poll from queue '#{name}' with #{
|
127
|
-
if
|
128
|
-
queue_item =
|
106
|
+
if queue_exists? name
|
107
|
+
@log.debug("[QueueManager] Poll from queue '#{name}' with #{queue(name).size} messages")
|
108
|
+
if queue(name).respond_to? :poll
|
109
|
+
queue_item = queue(name).poll
|
129
110
|
else
|
130
111
|
raise QueueManagerException.new("[QueueManager] You can't poll from queue '#{name}'", caller)
|
131
112
|
end
|
@@ -141,7 +122,8 @@ module FreeMessageQueue
|
|
141
122
|
# will be generated if there isn't a queue with the passed name (path). Otherwise
|
142
123
|
# it will raise a QueueManagerException if the passed queue doesn't exists.
|
143
124
|
def put(name, message)
|
144
|
-
|
125
|
+
# check for auto createing queues if they are not available
|
126
|
+
unless queue_exists? name
|
145
127
|
# only auto create queues if it is configured
|
146
128
|
if auto_create_queues?
|
147
129
|
create_queue(name)
|
@@ -149,22 +131,10 @@ module FreeMessageQueue
|
|
149
131
|
raise QueueManagerException.new("[QueueManager] There is no queue '#{name}'", caller)
|
150
132
|
end
|
151
133
|
end
|
152
|
-
|
153
|
-
# check max size constraints
|
154
|
-
if @queue_constraints[name][:max_size] != INFINITE &&
|
155
|
-
@queue_constraints[name][:max_size] < queue[name].bytes + message.bytes
|
156
|
-
raise QueueManagerException.new("[QueueManager] The queue '#{name}' is full, max amount of space (#{@queue_constraints[name][:max_size]}) is exceeded", caller)
|
157
|
-
end
|
158
|
-
|
159
|
-
# check max messages constraints
|
160
|
-
if @queue_constraints[name][:max_messages] != INFINITE &&
|
161
|
-
@queue_constraints[name][:max_messages] < queue[name].size + 1
|
162
|
-
raise QueueManagerException.new("[QueueManager] The queue '#{name}' is full, max amount of messages (#{@queue_constraints[name][:max_messages]}) is exceeded", caller)
|
163
|
-
end
|
164
134
|
|
165
|
-
@log.debug("[QueueManager] put message to queue '#{name}' with #{
|
166
|
-
if
|
167
|
-
|
135
|
+
@log.debug("[QueueManager] put message to queue '#{name}' with #{queue(name).size} messages")
|
136
|
+
if queue(name).respond_to? :put
|
137
|
+
queue(name).put(message)
|
168
138
|
else
|
169
139
|
raise QueueManagerException.new("[QueueManager] You can't put to queue '#{name}'", caller)
|
170
140
|
end
|
@@ -174,22 +144,12 @@ module FreeMessageQueue
|
|
174
144
|
|
175
145
|
# Returns the names (paths) of all queues managed by this queue manager
|
176
146
|
def queues
|
177
|
-
@
|
178
|
-
end
|
179
|
-
|
180
|
-
# Returns the size (number of messages)
|
181
|
-
def queue_size(name)
|
182
|
-
@queue[name].size
|
183
|
-
end
|
184
|
-
|
185
|
-
# Returns the byte size of the queue
|
186
|
-
def queue_bytes(name)
|
187
|
-
@queue[name].bytes
|
147
|
+
@queues.keys
|
188
148
|
end
|
189
149
|
|
190
150
|
# Is the name (path) of the queue in use allready
|
191
151
|
def queue_exists?(name)
|
192
|
-
!queue
|
152
|
+
!queue(name).nil?
|
193
153
|
end
|
194
154
|
|
195
155
|
# setup a this queue manager block need to be passed
|
@@ -197,63 +157,23 @@ module FreeMessageQueue
|
|
197
157
|
config.call(self)
|
198
158
|
end
|
199
159
|
|
200
|
-
|
201
|
-
|
202
|
-
# create a queue from a configuration hash.
|
203
|
-
# The <em>queue_config</em> contains the following parameter:
|
204
|
-
# * path: the path to the queue (with leading "/" and 3 characters at minimum) e.g. "/test_queue"
|
205
|
-
# * [optional] max_size: the maximum size e.g. "10mb", "100kb", "2gb" or (black or -1) for infinite
|
206
|
-
# * [optional] max_messages: the maximim messages that can be in the queue e.g. 1500 or (black or -1) for infinite
|
207
|
-
# All other parameter will be send to the queue directly using a naming convention. So if you have the extra parameter
|
208
|
-
# expire_date = "1h"
|
209
|
-
# the QueueManager will set the expire date using this assignment
|
210
|
-
# queue.expire_date = "1h"
|
211
|
-
# therefore your queue must implement this method
|
212
|
-
# def expire_date=(time)
|
213
|
-
# @expires_after = parse_seconds(time)
|
214
|
-
# end
|
215
|
-
def create_queue_from_config(queue_class, queue_config)
|
216
|
-
# path need to be specified
|
217
|
-
raise QueueManagerException.new("[QueueManager] There is now path specified for queue", caller) if queue_config[:path].nil?
|
218
|
-
path = queue_config[:path]
|
219
|
-
queue_config.delete :path
|
220
|
-
|
221
|
-
@log.debug("[QueueManager] setup queue from config '#{path}'")
|
222
|
-
|
223
|
-
# set max size parameter -- this parameter is optional
|
224
|
-
max_size = str_bytes(queue_config[:max_size])
|
225
|
-
max_size = INFINITE if max_size.nil? || max_size <= 0
|
226
|
-
queue_config.delete :max_size
|
227
|
-
|
228
|
-
# set max messages parameter -- this parameter is optional
|
229
|
-
max_messages = queue_config[:max_messages].to_i
|
230
|
-
max_messages = INFINITE if max_messages.nil? || max_messages <= 0
|
231
|
-
queue_config.delete :max_messages
|
232
|
-
|
233
|
-
queue = create_queue(path, max_messages, max_size, queue_class)
|
234
|
-
|
235
|
-
if queue_config.size > 0
|
236
|
-
@log.debug("[QueueManager] Configure addional parameters for queue '#{path}'; parameter: #{queue_config.inspect}")
|
237
|
-
for method_name in queue_config.keys
|
238
|
-
queue.send(method_name.to_s + "=", queue_config[method_name])
|
239
|
-
end
|
240
|
-
end
|
160
|
+
def queue(name)
|
161
|
+
return @queues[name]
|
241
162
|
end
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
bs
|
163
|
+
|
164
|
+
private
|
165
|
+
|
166
|
+
# Create a queue (<em>name</em> => <em>path</em>). The path must contain a leading "/" and a 3 character name
|
167
|
+
# at minimum. Exceptions will be raised if the queue allready exists.
|
168
|
+
def check_queue_name(name)
|
169
|
+
# path must begin with /
|
170
|
+
raise QueueManagerException.new("[QueueManager] Leading / in path '#{name}' missing", caller) if name[0..0] != "/"
|
171
|
+
|
172
|
+
# path must have a minimus lenght of 3 character
|
173
|
+
raise QueueManagerException.new("[QueueManager] The queue path '#{name}' is to short 3 character is minimum", caller) if name.size - 1 < 3
|
174
|
+
|
175
|
+
# don't create a queue twice
|
176
|
+
raise QueueManagerException.new("[QueueManager] The queue '#{name}' allready exists", caller) if queue_exists? name
|
257
177
|
end
|
258
178
|
end
|
259
179
|
end
|
data/lib/fmq/queues/README.txt
CHANGED
@@ -20,8 +20,8 @@ First of all, this template is where you start.
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def poll
|
23
|
-
|
24
|
-
|
25
|
-
|
23
|
+
FreeMessageQueue::Message.new "Hello World", "text/plain" do |m|
|
24
|
+
m.option["Time"] = Time.now
|
25
|
+
end
|
26
26
|
end
|
27
27
|
end
|
data/lib/fmq/queues/base.rb
CHANGED
@@ -31,26 +31,53 @@ module FreeMessageQueue
|
|
31
31
|
@created_at = created_at
|
32
32
|
@content_type = content_type
|
33
33
|
@option = {}
|
34
|
+
if block_given? then
|
35
|
+
yield self
|
36
|
+
end
|
34
37
|
end
|
35
38
|
|
36
|
-
#
|
39
|
+
# Size of item in bytes
|
37
40
|
def bytes
|
38
41
|
@payload.size
|
39
42
|
end
|
40
43
|
end
|
41
44
|
|
45
|
+
# All queue exceptions are raised using this class
|
46
|
+
class QueueException < Exception
|
47
|
+
attr_accessor :message, :backtrace
|
48
|
+
|
49
|
+
# Create exception with message and backtrace (if needed)
|
50
|
+
def initialize(message, callstack = [])
|
51
|
+
@message = message
|
52
|
+
@backtrace = callstack
|
53
|
+
end
|
54
|
+
|
55
|
+
# Returns the message of the exception
|
56
|
+
def to_s
|
57
|
+
@message
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
42
61
|
# every queue has to have this interface
|
43
62
|
class BaseQueue
|
44
63
|
# QueueManager refrence
|
45
64
|
attr_accessor :manager
|
65
|
+
|
66
|
+
# This value is used to decribe that a constraint has no limit e.g.
|
67
|
+
# max_messages = INFINITE
|
68
|
+
# means that there is no limitation for messages (by count)
|
69
|
+
INFINITE = -1
|
46
70
|
|
47
71
|
attr_reader :bytes, # the amount of space that is used by all messages in the queue
|
48
|
-
:size # the size / depp of the queue = count of messages
|
72
|
+
:size, # the size / depp of the queue = count of messages
|
73
|
+
:max_messages, # the max count of messages that can be in the queue
|
74
|
+
:max_size # the max size (bytes) of messages that can be in the queue
|
49
75
|
|
50
76
|
def initialize(manager)
|
51
77
|
@manager = manager
|
52
78
|
@bytes = 0
|
53
79
|
@size = 0
|
80
|
+
@max_size = @max_messages = INFINITE
|
54
81
|
end
|
55
82
|
|
56
83
|
# returns true if there is no message in the queue
|
@@ -58,18 +85,55 @@ module FreeMessageQueue
|
|
58
85
|
size == 0
|
59
86
|
end
|
60
87
|
|
88
|
+
# check that one can only set valid constraints
|
89
|
+
def max_messages=(val)
|
90
|
+
@max_messages = (val <= 0) ? BaseQueue::INFINITE : val
|
91
|
+
end
|
92
|
+
|
93
|
+
# check that one can only set valid constraints
|
94
|
+
def max_size=(val)
|
95
|
+
@max_size = (val <= 0) ? BaseQueue::INFINITE : val
|
96
|
+
end
|
97
|
+
|
61
98
|
protected
|
62
99
|
|
63
|
-
# update queue size and memory usage (add one
|
64
|
-
def add_message(
|
100
|
+
# update queue size and memory usage (add one message)
|
101
|
+
def add_message(message)
|
102
|
+
check_constraints_with_new(message)
|
103
|
+
|
65
104
|
@size += 1
|
66
|
-
@bytes += bytes
|
105
|
+
@bytes += message.bytes
|
106
|
+
return message
|
67
107
|
end
|
68
108
|
|
69
|
-
# update queue size and memory usage (remove one
|
70
|
-
def remove_message(
|
109
|
+
# update queue size and memory usage (remove one message)
|
110
|
+
def remove_message(message)
|
71
111
|
@size -= 1
|
72
|
-
@bytes -= bytes
|
112
|
+
@bytes -= message.bytes
|
113
|
+
return message
|
114
|
+
end
|
115
|
+
|
116
|
+
# check all constraints that are available.
|
117
|
+
# throws an exception if a constraint failed
|
118
|
+
def check_constraints_with_new(message)
|
119
|
+
check_max_size_constraint(message)
|
120
|
+
check_max_messages_constraint(message)
|
121
|
+
end
|
122
|
+
|
123
|
+
# check if max size of messages will exceed with new message.
|
124
|
+
# throws an exception if a constraint failed
|
125
|
+
def check_max_size_constraint(message)
|
126
|
+
if @max_size != INFINITE && (@max_size < self.bytes + message.bytes)
|
127
|
+
raise QueueException.new("[Queue] The queue is full, max amount of space (#{@max_size} bytes) is exceeded", caller)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
# check if max count of messages will exceed with new message.
|
132
|
+
# throws an exception if a constraint failed
|
133
|
+
def check_max_messages_constraint(message)
|
134
|
+
if @max_messages != INFINITE && (@max_messages < self.size + 1)
|
135
|
+
raise QueueException.new("[Queue] The queue is full, max amount of messages (#{@max_messages}) is exceeded", caller)
|
136
|
+
end
|
73
137
|
end
|
74
138
|
end
|
75
139
|
end
|
data/lib/fmq/queues/linked.rb
CHANGED
@@ -37,6 +37,8 @@ module FreeMessageQueue
|
|
37
37
|
def put(message)
|
38
38
|
return false if message == nil
|
39
39
|
|
40
|
+
add_message(message) # update stats and check constraints
|
41
|
+
|
40
42
|
# insert at end of list
|
41
43
|
if @first_message == nil
|
42
44
|
# first and last item are same if there is no item to the queue
|
@@ -46,7 +48,7 @@ module FreeMessageQueue
|
|
46
48
|
@last_message = @last_message.next = message
|
47
49
|
end
|
48
50
|
|
49
|
-
|
51
|
+
return true
|
50
52
|
end
|
51
53
|
|
52
54
|
# Return an message from the queue or nil if the queue is empty
|
@@ -59,9 +61,7 @@ module FreeMessageQueue
|
|
59
61
|
@first_message = message.next
|
60
62
|
@last_message = nil if @first_message.nil?
|
61
63
|
message.next = nil # unlink the message
|
62
|
-
remove_message(message
|
63
|
-
|
64
|
-
return message
|
64
|
+
remove_message(message) # update stats
|
65
65
|
else
|
66
66
|
return nil
|
67
67
|
end
|
@@ -75,6 +75,16 @@ module FreeMessageQueue
|
|
75
75
|
@queues[next_put_index].put(data)
|
76
76
|
end
|
77
77
|
|
78
|
+
# queue has infinite count
|
79
|
+
def max_messages
|
80
|
+
BaseQueue::INFINITE
|
81
|
+
end
|
82
|
+
|
83
|
+
# queue has infinite messages
|
84
|
+
def max_size
|
85
|
+
BaseQueue::INFINITE
|
86
|
+
end
|
87
|
+
|
78
88
|
private
|
79
89
|
|
80
90
|
# next index acts like 'round robin'
|
data/lib/fmq/server.rb
CHANGED
@@ -34,11 +34,11 @@ module FreeMessageQueue
|
|
34
34
|
end
|
35
35
|
|
36
36
|
# Process incoming request and send them to the right sub processing method like <em>process_get</em>
|
37
|
-
def call(env)
|
37
|
+
def call(env)
|
38
38
|
request = Rack::Request.new(env)
|
39
39
|
queue_path = request.env["REQUEST_PATH"]
|
40
40
|
@log.debug("[Server] Request params: #{YAML.dump(request.params)})")
|
41
|
-
|
41
|
+
|
42
42
|
response = nil
|
43
43
|
begin
|
44
44
|
# process supported features
|
@@ -50,9 +50,9 @@ module FreeMessageQueue
|
|
50
50
|
end
|
51
51
|
rescue QueueManagerException => ex
|
52
52
|
response = client_exception(request, queue_path, ex)
|
53
|
-
end
|
53
|
+
end
|
54
54
|
|
55
|
-
response.header["SERVER"] = SERVER_HEADER
|
55
|
+
response.header["SERVER"] = SERVER_HEADER
|
56
56
|
return response.finish
|
57
57
|
end
|
58
58
|
|
@@ -61,15 +61,15 @@ module FreeMessageQueue
|
|
61
61
|
# Returns an item from queue and sends it to the client.
|
62
62
|
# If there is no item to fetch send an 204 (NoContent) and same as HEAD
|
63
63
|
def process_get(request, queue_path)
|
64
|
-
message = @queue_manager.poll(queue_path)
|
64
|
+
message = @queue_manager.poll(queue_path)
|
65
65
|
|
66
66
|
unless message.nil? then
|
67
|
-
response = Rack::Response.new([], 200)
|
67
|
+
response = Rack::Response.new([], 200)
|
68
68
|
|
69
69
|
@log.debug("[Server] Response to GET (200)")
|
70
70
|
response.header["CONTENT-TYPE"] = message.content_type
|
71
|
-
response.header["QUEUE_SIZE"] = @queue_manager.
|
72
|
-
response.header["QUEUE_BYTES"] = @queue_manager.
|
71
|
+
response.header["QUEUE_SIZE"] = @queue_manager.queue(queue_path).size.to_s
|
72
|
+
response.header["QUEUE_BYTES"] = @queue_manager.queue(queue_path).bytes.to_s
|
73
73
|
|
74
74
|
# send all options of the message back to the client
|
75
75
|
if message.respond_to?(:option) && message.option.size > 0
|
@@ -86,13 +86,13 @@ module FreeMessageQueue
|
|
86
86
|
response = Rack::Response.new([], 204)
|
87
87
|
@log.debug("[Server] Response to GET (204)")
|
88
88
|
response.header["QUEUE_SIZE"] = response["QUEUE_BYTES"] = 0.to_s
|
89
|
-
end
|
89
|
+
end
|
90
90
|
return response
|
91
91
|
end
|
92
92
|
|
93
93
|
# Put new item to the queue and and return sam e as head action (HTTP 200)
|
94
94
|
def process_post(request, queue_path)
|
95
|
-
@log.debug("[Server] Response to POST (200)")
|
95
|
+
@log.debug("[Server] Response to POST (200)")
|
96
96
|
post_body = request.body.read
|
97
97
|
message = Message.new(post_body, request.content_type)
|
98
98
|
@log.debug("[Server] Message payload: #{post_body}")
|
@@ -107,8 +107,8 @@ module FreeMessageQueue
|
|
107
107
|
@queue_manager.put(queue_path, message)
|
108
108
|
|
109
109
|
response = Rack::Response.new([], 200)
|
110
|
-
response.header["QUEUE_SIZE"] = @queue_manager.
|
111
|
-
response.header["QUEUE_BYTES"] = @queue_manager.
|
110
|
+
response.header["QUEUE_SIZE"] = @queue_manager.queue(queue_path).size.to_s
|
111
|
+
response.header["QUEUE_BYTES"] = @queue_manager.queue(queue_path).bytes.to_s
|
112
112
|
return response
|
113
113
|
end
|
114
114
|
|
@@ -117,8 +117,8 @@ module FreeMessageQueue
|
|
117
117
|
@log.debug("[Server] Response to HEAD (200)")
|
118
118
|
|
119
119
|
response = Rack::Response.new([], 200)
|
120
|
-
response.header["QUEUE_SIZE"] = @queue_manager.
|
121
|
-
response.header["QUEUE_BYTES"] = @queue_manager.
|
120
|
+
response.header["QUEUE_SIZE"] = @queue_manager.queue(queue_path).size.to_s
|
121
|
+
response.header["QUEUE_BYTES"] = @queue_manager.queue(queue_path).bytes.to_s
|
122
122
|
return response
|
123
123
|
end
|
124
124
|
|
@@ -134,11 +134,11 @@ module FreeMessageQueue
|
|
134
134
|
# HTTP-Header field Error contains information about the problem.
|
135
135
|
# The client errorwill also be reported to warn level of logger.
|
136
136
|
def client_exception(request, queue_path, ex)
|
137
|
-
@log.warn("[Server] Client error: #{ex}")
|
137
|
+
@log.warn("[Server] Client error: #{ex}")
|
138
138
|
|
139
139
|
response = Rack::Response.new([], 400)
|
140
|
-
response.header["ERROR"] = ex.message
|
140
|
+
response.header["ERROR"] = ex.message
|
141
141
|
return response
|
142
142
|
end
|
143
143
|
end
|
144
|
-
end
|
144
|
+
end
|
data/lib/fmq/version.rb
CHANGED
data/test/test_basic.rb
CHANGED
@@ -26,11 +26,61 @@ class TestMessage < Test::Unit::TestCase
|
|
26
26
|
end
|
27
27
|
|
28
28
|
class BaseQueue < Test::Unit::TestCase
|
29
|
-
def
|
30
|
-
manager =
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
29
|
+
def setup
|
30
|
+
@manager = FreeMessageQueue::QueueManager.new()
|
31
|
+
|
32
|
+
@manager.setup do |m|
|
33
|
+
@queue1 = m.setup_queue "/dummy1"
|
34
|
+
@queue2 = m.setup_queue "/dummy2" do |q|
|
35
|
+
q.max_messages = 100
|
36
|
+
q.max_size = 100.kb
|
37
|
+
end
|
38
|
+
@queue3 = m.setup_queue "/dummy3" do |q|
|
39
|
+
q.max_size = 0
|
40
|
+
q.max_messages = -10
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_creating
|
46
|
+
# check first queue
|
47
|
+
assert_equal 0, @queue1.size
|
48
|
+
assert_equal 0, @queue1.bytes
|
49
|
+
assert_equal FreeMessageQueue::BaseQueue::INFINITE, @queue1.max_messages
|
50
|
+
assert_equal FreeMessageQueue::BaseQueue::INFINITE, @queue1.max_size
|
51
|
+
|
52
|
+
# check 2. queue
|
53
|
+
assert_equal 0, @queue2.size
|
54
|
+
assert_equal 0, @queue2.bytes
|
55
|
+
assert_equal 100, @queue2.max_messages
|
56
|
+
assert_equal 100 * 1024, @queue2.max_size
|
57
|
+
|
58
|
+
# check 3. queue
|
59
|
+
assert_equal 0, @queue3.size
|
60
|
+
assert_equal 0, @queue3.bytes
|
61
|
+
assert_equal FreeMessageQueue::BaseQueue::INFINITE, @queue3.max_messages
|
62
|
+
assert_equal FreeMessageQueue::BaseQueue::INFINITE, @queue3.max_size
|
63
|
+
|
64
|
+
assert_equal @manager, @queue1.manager
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_constraint_max_messages
|
68
|
+
100.times do |v|
|
69
|
+
@queue2.put(new_msg("test"))
|
70
|
+
end
|
71
|
+
|
72
|
+
assert_raise(FreeMessageQueue::QueueException) do
|
73
|
+
@queue2.put(new_msg("test"))
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_constraint_max_size
|
78
|
+
11.times do |v|
|
79
|
+
@queue2.put(new_msg("X" * 9.kb))
|
80
|
+
end
|
81
|
+
|
82
|
+
assert_raise(FreeMessageQueue::QueueException) do
|
83
|
+
@queue2.put(new_msg("X" * 10.kb))
|
84
|
+
end
|
35
85
|
end
|
36
86
|
end
|
data/test/test_queue_manager.rb
CHANGED
@@ -12,54 +12,58 @@ class TestQueueManager < Test::Unit::TestCase
|
|
12
12
|
@queue_manager.setup do |qm|
|
13
13
|
qm.auto_create_queues = false
|
14
14
|
|
15
|
-
qm.setup_queue do |q|
|
16
|
-
q.path = DEFAULT_QUEUE_NAME
|
15
|
+
qm.setup_queue DEFAULT_QUEUE_NAME do |q|
|
17
16
|
q.max_messages = 100
|
18
|
-
q.max_size =
|
17
|
+
q.max_size = 100.mb
|
19
18
|
end
|
19
|
+
|
20
|
+
qm.setup_queue "/second_test_queue"
|
21
|
+
qm.setup_queue "/third_test_queue"
|
20
22
|
end
|
21
23
|
end
|
22
24
|
|
23
25
|
def test_config
|
24
|
-
# check constraints
|
25
|
-
constraints = { :max_messages => 100, :max_size => 104857600 }
|
26
|
-
assert_equal constraints, @queue_manager.queue_constraints[DEFAULT_QUEUE_NAME]
|
27
|
-
|
28
26
|
# check that the simple config will work
|
29
27
|
FreeMessageQueue::QueueManager.new()
|
30
28
|
@queue_manager.setup do |qm|
|
31
29
|
qm.auto_create_queues = false
|
32
30
|
end
|
31
|
+
|
32
|
+
# check if all queues are available
|
33
|
+
assert_equal 3, @queue_manager.queues.size
|
34
|
+
[DEFAULT_QUEUE_NAME, "/second_test_queue", "/third_test_queue"].each do |e|
|
35
|
+
assert @queue_manager.queues.include? e
|
36
|
+
end
|
33
37
|
end
|
34
38
|
|
35
39
|
def test_poll_and_get
|
36
40
|
10.times do
|
37
41
|
@queue_manager.put(DEFAULT_QUEUE_NAME, new_msg("XXX" * 20))
|
38
42
|
end
|
39
|
-
assert_equal 10, @queue_manager.
|
43
|
+
assert_equal 10, @queue_manager.queue(DEFAULT_QUEUE_NAME).size
|
40
44
|
i = 0
|
41
45
|
while message = @queue_manager.poll(DEFAULT_QUEUE_NAME)
|
42
46
|
i += 1
|
43
47
|
end
|
44
48
|
assert_equal 10, i
|
45
|
-
assert_equal 0, @queue_manager.
|
49
|
+
assert_equal 0, @queue_manager.queue(DEFAULT_QUEUE_NAME).size
|
46
50
|
|
47
51
|
# should raise a exception because of the maximum messages limitation
|
48
52
|
i = 0
|
49
|
-
assert_raise(FreeMessageQueue::
|
53
|
+
assert_raise(FreeMessageQueue::QueueException) {
|
50
54
|
101.times do
|
51
55
|
@queue_manager.put(DEFAULT_QUEUE_NAME, new_msg("XXX" * 20))
|
52
56
|
i += 1
|
53
57
|
end
|
54
58
|
}
|
55
|
-
@queue_manager.queue
|
56
|
-
assert_equal 0, @queue_manager.
|
59
|
+
@queue_manager.queue(DEFAULT_QUEUE_NAME).clear
|
60
|
+
assert_equal 0, @queue_manager.queue(DEFAULT_QUEUE_NAME).size
|
57
61
|
assert i = 100
|
58
62
|
|
59
63
|
# should raise a exception because of the maximum byte size limitation
|
60
64
|
i = 0
|
61
65
|
two_mb_message = new_msg("X" * 1024 * 1024 * 2)
|
62
|
-
assert_raise(FreeMessageQueue::
|
66
|
+
assert_raise(FreeMessageQueue::QueueException) {
|
63
67
|
101.times do
|
64
68
|
@queue_manager.put(DEFAULT_QUEUE_NAME, two_mb_message)
|
65
69
|
i += 1
|
@@ -70,7 +74,7 @@ class TestQueueManager < Test::Unit::TestCase
|
|
70
74
|
|
71
75
|
def test_creating_and_deleting
|
72
76
|
url = "/XX123"
|
73
|
-
@queue_manager.
|
77
|
+
@queue_manager.setup_queue url
|
74
78
|
@queue_manager.put(url, new_msg("X_X_X_X_X_X"))
|
75
79
|
assert_equal "X_X_X_X_X_X", @queue_manager.poll(url).payload
|
76
80
|
@queue_manager.delete_queue(url)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fmq
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Vincent Landgraf
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-08-
|
12
|
+
date: 2008-08-27 00:00:00 +02:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|