fmq 0.3.0 → 0.3.1

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/History.txt CHANGED
@@ -1,4 +1,14 @@
1
- == 0.3.0 2008-08-06
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
@@ -1,4 +1,8 @@
1
- require "fmq"
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
- qm.setup_queue do |q|
28
- # the path to the queue e.g. /app1/myframe/test1
29
- # means http://localhost:5884/app1/myframe/test1
30
- # this parameter is not optional
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 = 1000000
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 = "10kb"
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 do |q|
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 do |q|
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
- msg = FreeMessageQueue::Message.new "Hello World", "text/plain"
8
- msg.option["Time"] = Time.now
9
- msg
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.create_queue(request["path"],
66
- request["max_messages"].gsub("null", "-1").to_i,
67
- request["max_size"].gsub("null", "-1").to_i)
68
- return OK
69
- else
70
- return WRONG_METHOD
71
- end
72
- else
73
- return WRONG_METHOD
74
- end
75
- rescue QueueManagerException => ex
76
- return [400, {"CONTENT-TYPE" => "text/plain", "ERROR" => ex.message}, [ex.message]]
77
- end
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
- constraints = @manager.queue_constraints[queue_name]
85
+ queue = @manager.queue(queue_name)
85
86
 
86
87
  "[\"%s\", %d, %d, %d, %d]" % [
87
88
  queue_name,
88
- @manager.queue[queue_name].bytes,
89
- constraints[:max_size],
90
- @manager.queue[queue_name].size,
91
- constraints[:max_messages],
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
+
@@ -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
- @queue = {}
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, &configure_queue)
106
- queue_settings = OpenStruct.new
107
- configure_queue.call(queue_settings)
108
- create_queue_from_config(queue_class, queue_settings.marshal_dump)
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 @queue[name]
114
- @log.info("[QueueManager] Delete queue '#{name}' with #{@queue[name].size} messages")
115
- @queue[name].clear
116
- @queue.delete name
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 @queue[name]
126
- @log.debug("[QueueManager] Poll from queue '#{name}' with #{@queue[name].size} messages")
127
- if @queue[name].respond_to? :poll
128
- queue_item = @queue[name].poll
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
- unless @queue[name]
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 #{@queue[name].size} messages")
166
- if @queue[name].respond_to? :put
167
- @queue[name].put(message)
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
- @queue.keys
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[name].nil?
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
- private
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
- # Retuns count of bytes to a expression with kb, mb or gb
244
- # e.g 10kb will return 10240
245
- def str_bytes(str)
246
- case str
247
- when /([0-9]+)kb/i
248
- bs = $1.to_i * 1024
249
- when /([0-9]+)mb/i
250
- bs = $1.to_i * 1024 * 1024
251
- when /([0-9]+)gb/i
252
- bs = $1.to_i * 1024 * 1024 * 1024
253
- else
254
- bs = INFINITE
255
- end
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
@@ -20,8 +20,8 @@ First of all, this template is where you start.
20
20
  end
21
21
 
22
22
  def poll
23
- msg = FreeMessageQueue::Message.new "Hello World", "text/plain"
24
- msg.option["Time"] = Time.now
25
- msg
23
+ FreeMessageQueue::Message.new "Hello World", "text/plain" do |m|
24
+ m.option["Time"] = Time.now
25
+ end
26
26
  end
27
27
  end
@@ -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
- # Aize of item in bytes
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 messge)
64
- def add_message(bytes)
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 messge)
70
- def remove_message(bytes)
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
@@ -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
- add_message(message.bytes) # update stats
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.bytes) # update stats
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.queue_size(queue_path).to_s
72
- response.header["QUEUE_BYTES"] = @queue_manager.queue_bytes(queue_path).to_s
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.queue_size(queue_path).to_s
111
- response.header["QUEUE_BYTES"] = @queue_manager.queue_bytes(queue_path).to_s
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.queue_size(queue_path).to_s
121
- response.header["QUEUE_BYTES"] = @queue_manager.queue_bytes(queue_path).to_s
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
@@ -20,7 +20,7 @@ module FreeMessageQueue
20
20
  module VERSION #:nodoc:
21
21
  MAJOR = 0
22
22
  MINOR = 3
23
- TINY = 0
23
+ TINY = 1
24
24
 
25
25
  STRING = [MAJOR, MINOR, TINY].join('.')
26
26
  end
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 test_initialize
30
- manager = "pseudo manager"
31
- queue = FreeMessageQueue::BaseQueue.new(manager)
32
- assert_equal 0, queue.size
33
- assert_equal 0, queue.bytes
34
- assert_equal manager, queue.manager
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
@@ -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 = "100mb"
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.queue_size(DEFAULT_QUEUE_NAME)
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.queue_size(DEFAULT_QUEUE_NAME)
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::QueueManagerException) {
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[DEFAULT_QUEUE_NAME].clear
56
- assert_equal 0, @queue_manager.queue_size(DEFAULT_QUEUE_NAME)
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::QueueManagerException) {
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.create_queue(url)
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.0
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-26 00:00:00 +02:00
12
+ date: 2008-08-27 00:00:00 +02:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency