fmq 0.1.0
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/bin/fmq +37 -0
- data/default-server/admin-interface/images/logo.png +0 -0
- data/default-server/admin-interface/index.html +240 -0
- data/default-server/admin-interface/prototype.js +4221 -0
- data/default-server/config.yml +66 -0
- data/default-server/queues/my_test.rb +20 -0
- data/lib/fmq/boot.rb +99 -0
- data/lib/fmq/client.rb +43 -0
- data/lib/fmq/mongrel_server.rb +142 -0
- data/lib/fmq/queue_manager.rb +206 -0
- data/lib/fmq/queues/admin.rb +88 -0
- data/lib/fmq/queues/file.rb +54 -0
- data/lib/fmq/queues/linked.rb +92 -0
- data/lib/fmq/queues/load_balanced.rb +87 -0
- data/lib/fmq/queues/syncronized.rb +43 -0
- data/lib/fmq/version.rb +27 -0
- data/lib/fmq.rb +33 -0
- data/setup.rb +1585 -0
- data/test/test_fmq_client.rb +26 -0
- data/test/test_fmq_queue.rb +47 -0
- data/test/test_helper.rb +2 -0
- metadata +77 -0
@@ -0,0 +1,66 @@
|
|
1
|
+
server:
|
2
|
+
# start the server on every nic
|
3
|
+
interface: 0.0.0.0
|
4
|
+
# this option set the port for the FMQ
|
5
|
+
port: 5884
|
6
|
+
# here you can setup the log level to see what is going wrong if needed
|
7
|
+
log-level: info
|
8
|
+
|
9
|
+
queue-manager:
|
10
|
+
# if someone pushes to a queue that don't exists
|
11
|
+
# the queue manager will create one for you. This
|
12
|
+
# is a good option if you are in development
|
13
|
+
# the queue will be a LoadBalancedQueue by default
|
14
|
+
auto-create-queues: true
|
15
|
+
# if you want some queues right from startup
|
16
|
+
# define there url and constraints here
|
17
|
+
defined-queues:
|
18
|
+
# queue names are not relevant use them to
|
19
|
+
# organize your queues
|
20
|
+
test-queue-1:
|
21
|
+
# the path to the queue e.g. /app1/myframe/test1
|
22
|
+
# means http://localhost:5884/app1/myframe/test1
|
23
|
+
# this parameter is not optional
|
24
|
+
path: /fmq_test/test1
|
25
|
+
# this defines the maximum count of messages that
|
26
|
+
# can be in the queue, if the queue is full every
|
27
|
+
# new message will be rejected with a http error
|
28
|
+
# this parameter is optional if you don't specify
|
29
|
+
# a max value the queue size depends on your system
|
30
|
+
max-messages: 1000000
|
31
|
+
# this optional to and specifys the max content size
|
32
|
+
# for all data of a queue
|
33
|
+
# valid extensions are kb, mb, gb
|
34
|
+
max-size: 10kb
|
35
|
+
test-queue-2:
|
36
|
+
path: /fmq_test/test2
|
37
|
+
# if you want you can specify the class of the queue
|
38
|
+
# this is interessting if you write your own queues
|
39
|
+
class: FreeMessageQueue::LoadBalancedQueue
|
40
|
+
test-queue-3:
|
41
|
+
path: /fmq_test/test3
|
42
|
+
class: MyTestQueue
|
43
|
+
# the admin queue is a special queue, that is used to
|
44
|
+
# administrate the queue_manager
|
45
|
+
admin-page-backend:
|
46
|
+
path: /admin/queue
|
47
|
+
class: FreeMessageQueue::AdminQueue
|
48
|
+
filter: /admin
|
49
|
+
# The following 3 queues are specail queues to, they
|
50
|
+
# are the admin interface to the application and
|
51
|
+
# make use of the file queue
|
52
|
+
admin-page-index:
|
53
|
+
path: /admin/index
|
54
|
+
class: FreeMessageQueue::FileQueue
|
55
|
+
file: admin-interface/index.html
|
56
|
+
content-type: text/html
|
57
|
+
admin-page-logo:
|
58
|
+
path: /admin/images/logo.png
|
59
|
+
class: FreeMessageQueue::FileQueue
|
60
|
+
file: admin-interface/images/logo.png
|
61
|
+
content-type: image/png
|
62
|
+
admin-page-prototype:
|
63
|
+
path: /admin/prototype.js
|
64
|
+
class: FreeMessageQueue::FileQueue
|
65
|
+
file: admin-interface/prototype.js
|
66
|
+
content-type: text/javascript
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require "ostruct"
|
2
|
+
|
3
|
+
class MyTestQueue
|
4
|
+
attr_accessor :manager
|
5
|
+
attr_reader :bytes, :size
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@bytes = @size = 1
|
9
|
+
end
|
10
|
+
|
11
|
+
def put(data)
|
12
|
+
puts "NEW MESSAGE"
|
13
|
+
end
|
14
|
+
|
15
|
+
def poll
|
16
|
+
item = OpenStruct.new
|
17
|
+
item.data = "Hello World"
|
18
|
+
item
|
19
|
+
end
|
20
|
+
end
|
data/lib/fmq/boot.rb
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2008 Vincent Landgraf
|
3
|
+
#
|
4
|
+
# This file is part of the Free Message Queue.
|
5
|
+
#
|
6
|
+
# Foobar is free software: you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU General Public License as published by
|
8
|
+
# the Free Software Foundation, either version 3 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
#
|
11
|
+
# Foobar is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU General Public License
|
17
|
+
# along with Foobar. If not, see <http://www.gnu.org/licenses/>.
|
18
|
+
#
|
19
|
+
require 'yaml'
|
20
|
+
require 'logger'
|
21
|
+
require 'fileutils'
|
22
|
+
|
23
|
+
require File.dirname(__FILE__) + '/version'
|
24
|
+
require File.dirname(__FILE__) + '/queue_manager'
|
25
|
+
require File.dirname(__FILE__) + '/mongrel_server'
|
26
|
+
|
27
|
+
module FreeMessageQueue
|
28
|
+
SERVER_HEADER = "FMQ/#{FreeMessageQueue::VERSION::STRING} (#{RUBY_PLATFORM}) Ruby/#{RUBY_VERSION}"
|
29
|
+
|
30
|
+
def self.logger
|
31
|
+
$FMQ_GLOBAL_LOGGER
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.create_logger(log_to = STDOUT)
|
35
|
+
$FMQ_GLOBAL_LOGGER ||= Logger.new(log_to)
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.set_log_level(level)
|
39
|
+
case level
|
40
|
+
when /fatal/i
|
41
|
+
FreeMessageQueue.logger.level = Logger::FATAL
|
42
|
+
when /error/i
|
43
|
+
FreeMessageQueue.logger.level = Logger::ERROR
|
44
|
+
when /warn/i
|
45
|
+
FreeMessageQueue.logger.level = Logger::WARN
|
46
|
+
when /info/i
|
47
|
+
FreeMessageQueue.logger.level = Logger::INFO
|
48
|
+
when /debug/i
|
49
|
+
FreeMessageQueue.logger.level = Logger::DEBUG
|
50
|
+
end
|
51
|
+
FreeMessageQueue.logger.debug "[Logger] set log level to #{level}"
|
52
|
+
end
|
53
|
+
|
54
|
+
class Configuration
|
55
|
+
def initialize(file_path)
|
56
|
+
# open and read file
|
57
|
+
f = open(file_path, "r")
|
58
|
+
data = f.read
|
59
|
+
f.close
|
60
|
+
@config = YAML.load( data )
|
61
|
+
|
62
|
+
# set log level
|
63
|
+
FreeMessageQueue.set_log_level(server["log-level"])
|
64
|
+
|
65
|
+
# debug the configuration
|
66
|
+
FreeMessageQueue.logger.debug("[Configuration] Server: " + YAML.dump(server))
|
67
|
+
FreeMessageQueue.logger.debug("[Configuration] QueueManager: " + YAML.dump(queue_manager))
|
68
|
+
end
|
69
|
+
|
70
|
+
def server
|
71
|
+
@config["server"]
|
72
|
+
end
|
73
|
+
|
74
|
+
def queue_manager
|
75
|
+
@config["queue-manager"]
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
class Boot
|
80
|
+
def self.start_server
|
81
|
+
# create logger and setup log level
|
82
|
+
logger = FreeMessageQueue.create_logger
|
83
|
+
conf = FreeMessageQueue::Configuration.new("config.yml")
|
84
|
+
|
85
|
+
# create queue manager
|
86
|
+
queue_manager = FreeMessageQueue::QueueManager.new(conf.queue_manager)
|
87
|
+
|
88
|
+
# setup and run server
|
89
|
+
logger.debug "[MongrelHandler] startup at #{conf.server["interface"]}:#{conf.server["port"]}"
|
90
|
+
servsock = Mongrel::HttpServer.new(conf.server["interface"], conf.server["port"])
|
91
|
+
servsock.register("/", FreeMessageQueue::MongrelHandler.new(queue_manager))
|
92
|
+
servsock.run.join
|
93
|
+
end
|
94
|
+
|
95
|
+
def self.create_project(project_name)
|
96
|
+
FileUtils.cp_r(File.dirname(__FILE__) + '/../../default-server', project_name)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
data/lib/fmq/client.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2008 Vincent Landgraf
|
3
|
+
#
|
4
|
+
# This file is part of the Free Message Queue.
|
5
|
+
#
|
6
|
+
# Foobar is free software: you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU General Public License as published by
|
8
|
+
# the Free Software Foundation, either version 3 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
#
|
11
|
+
# Foobar is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU General Public License
|
17
|
+
# along with Foobar. If not, see <http://www.gnu.org/licenses/>.
|
18
|
+
#
|
19
|
+
require 'net/http'
|
20
|
+
|
21
|
+
module FreeMessageQueue
|
22
|
+
class ClientQueue
|
23
|
+
def initialize(url)
|
24
|
+
@url = url
|
25
|
+
end
|
26
|
+
|
27
|
+
def get()
|
28
|
+
url = URI.parse(@url)
|
29
|
+
req = Net::HTTP::Get.new(url.path)
|
30
|
+
res = Net::HTTP.start(url.host, url.port) do |http|
|
31
|
+
http.request(req)
|
32
|
+
end
|
33
|
+
res.body
|
34
|
+
end
|
35
|
+
|
36
|
+
def put(data)
|
37
|
+
url = URI.parse(@url)
|
38
|
+
res = Net::HTTP.start(url.host, url.port) do |http|
|
39
|
+
http.post(url.path, data)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
#!/usr/bin/env ruby -wKU
|
2
|
+
#
|
3
|
+
# Copyright (c) 2008 Vincent Landgraf
|
4
|
+
#
|
5
|
+
# This file is part of the Free Message Queue.
|
6
|
+
#
|
7
|
+
# Foobar is free software: you can redistribute it and/or modify
|
8
|
+
# it under the terms of the GNU General Public License as published by
|
9
|
+
# the Free Software Foundation, either version 3 of the License, or
|
10
|
+
# (at your option) any later version.
|
11
|
+
#
|
12
|
+
# Foobar is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
# GNU General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU General Public License
|
18
|
+
# along with Foobar. If not, see <http://www.gnu.org/licenses/>.
|
19
|
+
#
|
20
|
+
begin
|
21
|
+
require "mongrel"
|
22
|
+
rescue LoadError
|
23
|
+
require "rubygems"
|
24
|
+
require "mongrel"
|
25
|
+
end
|
26
|
+
|
27
|
+
module FreeMessageQueue
|
28
|
+
class MongrelHandler < Mongrel::HttpHandler
|
29
|
+
def initialize(queue_manager)
|
30
|
+
@queue_manager = queue_manager
|
31
|
+
@log = FreeMessageQueue.logger
|
32
|
+
end
|
33
|
+
|
34
|
+
def process(request, response)
|
35
|
+
queue_path = request.params["REQUEST_PATH"]
|
36
|
+
method = request.params["REQUEST_METHOD"]
|
37
|
+
@log.debug("[MongrelHandler] Incomming request for #{queue_path} [#{method}] (#{request.params["REMOTE_ADDR"]})")
|
38
|
+
@log.debug("[MongrelHandler] Request data: #{YAML.dump(request.params)})")
|
39
|
+
|
40
|
+
begin
|
41
|
+
# process supported features
|
42
|
+
if method.match(/^(GET|POST|HEAD|DELETE)$/) then
|
43
|
+
self.send("process_" + method.downcase, request, response, queue_path)
|
44
|
+
else
|
45
|
+
raise QueueManagerException.new("[MongrelHandler] Method is not supported '#{method}'", caller)
|
46
|
+
end
|
47
|
+
rescue QueueManagerException => ex
|
48
|
+
client_exception(request, response, queue_path, ex)
|
49
|
+
rescue => ex
|
50
|
+
server_exception(request, response, queue_path, ex)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
# returns an item from queue and sends it to the client
|
57
|
+
# if there is no item to fetch send an 204 (NoContent) and same as HEAD
|
58
|
+
def process_get(request, response, queue_path)
|
59
|
+
queue_item = @queue_manager.poll(queue_path)
|
60
|
+
|
61
|
+
if queue_item then
|
62
|
+
response.start(200) do |head,out|
|
63
|
+
@log.debug("[MongrelHandler] Response to GET (200)")
|
64
|
+
head["Content-Type"] = (queue_item.respond_to?(:content_type)) ? queue_item.content_type : "text/plain"
|
65
|
+
head["Server"] = SERVER_HEADER
|
66
|
+
head["Queue-Size"] = @queue_manager.queue_size(queue_path)
|
67
|
+
if !queue_item.data.nil? && queue_item.data.size > 0
|
68
|
+
@log.debug("[MongrelHandler] Response data: #{queue_item.data}")
|
69
|
+
out.write(queue_item.data)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
else
|
73
|
+
response.start(204) do |head,out|
|
74
|
+
@log.debug("[MongrelHandler] Response to GET (204)")
|
75
|
+
head["Server"] = SERVER_HEADER
|
76
|
+
head["Queue-Size"] = 0
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# put new item to the queue and and return sam e as head action
|
82
|
+
def process_post(request, response, queue_path)
|
83
|
+
@log.debug("[MongrelHandler] Response to POST (200)")
|
84
|
+
data = request.body.read
|
85
|
+
@log.debug("[MongrelHandler] DATA: #{data}")
|
86
|
+
@queue_manager.put(queue_path, data)
|
87
|
+
|
88
|
+
response.start(200) do |head,out|
|
89
|
+
head["Server"] = SERVER_HEADER
|
90
|
+
head["Queue-Size"] = @queue_manager.queue_size(queue_path)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# just return server header and queue size
|
95
|
+
def process_head(request, response, queue_path)
|
96
|
+
@log.debug("[MongrelHandler] Response to HEAD (200)")
|
97
|
+
|
98
|
+
response.start(200) do |head,out|
|
99
|
+
head["Server"] = SERVER_HEADER
|
100
|
+
head["Queue-Size"] = @queue_manager.queue_size(queue_path)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# delete the queue and return server header
|
105
|
+
def process_delete(request, response, queue_path)
|
106
|
+
@log.debug("[MongrelHandler] Response to DELETE (200)")
|
107
|
+
@queue_manager.delete_queue(queue_path)
|
108
|
+
|
109
|
+
response.start(200) do |head,out|
|
110
|
+
head["Server"] = SERVER_HEADER
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# inform the client that he did something wrong
|
115
|
+
def client_exception(request, response, queue_path, ex)
|
116
|
+
@log.warn("[MongrelHandler] Client error: #{ex}")
|
117
|
+
response.start(400) do |head,out|
|
118
|
+
head["Server"] = SERVER_HEADER
|
119
|
+
head["Error"] = ex.message
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# report server error
|
124
|
+
def server_exception(request, response, queue_path, ex)
|
125
|
+
@log.fatal("[MongrelHandler] System error: #{ex}")
|
126
|
+
for line in ex.backtrace
|
127
|
+
@log.error line
|
128
|
+
end
|
129
|
+
|
130
|
+
response.start(500) do |head,out|
|
131
|
+
head["Content-Type"] = "text/plain"
|
132
|
+
head["Server"] = SERVER_HEADER
|
133
|
+
head["Error"] = ex.message
|
134
|
+
|
135
|
+
out.write(ex.message + "\r\n\r\n")
|
136
|
+
for line in ex.backtrace
|
137
|
+
out.write(line + "\r\n")
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
@@ -0,0 +1,206 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2008 Vincent Landgraf
|
3
|
+
#
|
4
|
+
# This file is part of the Free Message Queue.
|
5
|
+
#
|
6
|
+
# Foobar is free software: you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU General Public License as published by
|
8
|
+
# the Free Software Foundation, either version 3 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
#
|
11
|
+
# Foobar is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU General Public License
|
17
|
+
# along with Foobar. If not, see <http://www.gnu.org/licenses/>.
|
18
|
+
#
|
19
|
+
module FreeMessageQueue
|
20
|
+
class QueueManagerException < RuntimeError
|
21
|
+
attr_accessor :message, :backtrace
|
22
|
+
|
23
|
+
def initialize(message, callstack)
|
24
|
+
@message = message
|
25
|
+
@backtrace = callstack
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_s
|
29
|
+
@message
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class QueueManager
|
34
|
+
INFINITE = -1
|
35
|
+
|
36
|
+
def initialize(config)
|
37
|
+
@queue = {}
|
38
|
+
@config = config
|
39
|
+
@queue_constraints = {}
|
40
|
+
@log = FreeMessageQueue.logger
|
41
|
+
setup_queue_manager()
|
42
|
+
end
|
43
|
+
|
44
|
+
def auto_create_queues?
|
45
|
+
@config["auto-create-queues"]
|
46
|
+
end
|
47
|
+
|
48
|
+
def create_queue(name, max_messages = INFINITE, max_size = INFINITE, default_class = FreeMessageQueue::SyncronizedQueue)
|
49
|
+
# path must begin with /
|
50
|
+
raise QueueManagerException.new("[QueueManager] Leading / in path '#{name}' missing", caller) if name[0..0] != "/"
|
51
|
+
|
52
|
+
# path must have a minimus lenght of 3 character
|
53
|
+
raise QueueManagerException.new("[QueueManager] The queue path '#{name}' is to short 3 character is minimum", caller) if name.size - 1 < 3
|
54
|
+
|
55
|
+
# don't create a queue twice
|
56
|
+
raise QueueManagerException.new("[QueueManager] The queue '#{name}' allready exists", caller) if queue_exists? name
|
57
|
+
|
58
|
+
@log.info("[QueueManager] Create queue '#{name}' {type: #{default_class}, max_messages: #{max_messages}, max_size: #{max_size}}")
|
59
|
+
|
60
|
+
@queue[name] = default_class.new
|
61
|
+
@queue[name].manager = self
|
62
|
+
@queue_constraints[name] = {
|
63
|
+
:max_messages => max_messages,
|
64
|
+
:max_size => max_size
|
65
|
+
}
|
66
|
+
|
67
|
+
@queue[name]
|
68
|
+
end
|
69
|
+
|
70
|
+
def delete_queue(name)
|
71
|
+
if @queue[name]
|
72
|
+
@log.info("[QueueManager] Delete queue '#{name}' with #{@queue[name].size} messages")
|
73
|
+
@queue[name].clear
|
74
|
+
@queue.delete name
|
75
|
+
true
|
76
|
+
else
|
77
|
+
raise QueueManagerException.new("[QueueManager] There is no queue '#{name}'", caller)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def poll(name)
|
82
|
+
if @queue[name]
|
83
|
+
@log.debug("[QueueManager] Poll from queue '#{name}' with #{@queue[name].size} messages")
|
84
|
+
queue_item = @queue[name].poll
|
85
|
+
else
|
86
|
+
raise QueueManagerException.new("[QueueManager] There is no queue '#{name}'", caller)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def put(name, data)
|
91
|
+
unless @queue[name]
|
92
|
+
# only auto create queues if it is configured
|
93
|
+
if auto_create_queues?
|
94
|
+
create_queue(name)
|
95
|
+
else
|
96
|
+
raise QueueManagerException.new("[QueueManager] There is no queue '#{name}'", caller)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# check max size constraints
|
101
|
+
if @queue_constraints[name][:max_size] != INFINITE &&
|
102
|
+
@queue_constraints[name][:max_size] < queue(name).bytes + data.size
|
103
|
+
raise QueueManagerException.new("[QueueManager] The queue '#{name}' is full, max amount of space (#{@queue_constraints[name][:max_size]}) is exceeded", caller)
|
104
|
+
end
|
105
|
+
|
106
|
+
# check max messages constraints
|
107
|
+
if @queue_constraints[name][:max_messages] != INFINITE &&
|
108
|
+
@queue_constraints[name][:max_messages] < queue(name).size + 1
|
109
|
+
raise QueueManagerException.new("[QueueManager] The queue '#{name}' is full, max amount of messages (#{@queue_constraints[name][:max_messages]}) is exceeded", caller)
|
110
|
+
end
|
111
|
+
|
112
|
+
@log.debug("[QueueManager] put message to queue '#{name}' with #{@queue[name].size} messages")
|
113
|
+
@queue[name].put(data)
|
114
|
+
end
|
115
|
+
|
116
|
+
def queues
|
117
|
+
@queue.keys
|
118
|
+
end
|
119
|
+
|
120
|
+
def queue_size(name)
|
121
|
+
@queue[name].size
|
122
|
+
end
|
123
|
+
|
124
|
+
def queue_constraints(name)
|
125
|
+
@queue_constraints[name]
|
126
|
+
end
|
127
|
+
|
128
|
+
def queue(name)
|
129
|
+
@queue[name]
|
130
|
+
end
|
131
|
+
|
132
|
+
def queue_exists?(name)
|
133
|
+
!queue(name).nil?
|
134
|
+
end
|
135
|
+
|
136
|
+
# create a queue from a configuration hash
|
137
|
+
# <em>queue_name</em> this name is just for debugging and organizing the queue
|
138
|
+
# <em>queue_config</em> here you will pass the parameter:
|
139
|
+
# * path:
|
140
|
+
# * [optional] max-size: the maximum size e.g. 10mb
|
141
|
+
# * [optional] max-messages: the maximim messages that can be in the queue e.g. 9999999
|
142
|
+
# * [optional] class: the class that implements this queue e.g. FreeMessageQueue::SystemQueue
|
143
|
+
def create_queue_from_config(queue_name, queue_config)
|
144
|
+
@log.debug("[QueueManager] setup queue from config '#{queue_name}'")
|
145
|
+
|
146
|
+
# path need to be specified
|
147
|
+
raise QueueManagerException.new("[QueueManager] There is now path specified for queue '#{queue_name}'", caller) if queue_config["path"].nil?
|
148
|
+
path = queue_config["path"]
|
149
|
+
queue_config.delete "path"
|
150
|
+
|
151
|
+
# set max size parameter -- this parameter is optional
|
152
|
+
max_size = str_bytes(queue_config["max-size"])
|
153
|
+
max_size = INFINITE if max_size.nil? || max_size <= 0
|
154
|
+
queue_config.delete "max-size"
|
155
|
+
|
156
|
+
# set max messages parameter -- this parameter is optional
|
157
|
+
max_messages = queue_config["max-messages"].to_i
|
158
|
+
max_messages = INFINITE if max_messages.nil? || max_messages <= 0
|
159
|
+
queue_config.delete "max-messages"
|
160
|
+
|
161
|
+
# set class parameter -- this parameter is optional
|
162
|
+
default_class = queue_config["class"]
|
163
|
+
default_class = eval(default_class) unless default_class.nil?
|
164
|
+
queue_config.delete "class"
|
165
|
+
|
166
|
+
if default_class.nil?
|
167
|
+
queue = create_queue(path, max_messages, max_size)
|
168
|
+
else
|
169
|
+
queue = create_queue(path, max_messages, max_size, default_class)
|
170
|
+
end
|
171
|
+
|
172
|
+
if queue_config.size > 0
|
173
|
+
@log.debug("[QueueManager] Configure addional parameters for queue '#{queue_name}'; parameter: #{queue_config.inspect}")
|
174
|
+
for parameter in queue_config.keys
|
175
|
+
method_name = parameter.gsub("-", "_")
|
176
|
+
queue.send(method_name + "=", queue_config[parameter])
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
private
|
182
|
+
# retuns count of bytes to a expression with kb, mb or gb
|
183
|
+
# e.g 10kb will return 10240
|
184
|
+
def str_bytes(str)
|
185
|
+
case str
|
186
|
+
when /([0-9]+)kb/i
|
187
|
+
bs = $1.to_i * 1024
|
188
|
+
when /([0-9]+)mb/i
|
189
|
+
bs = $1.to_i * 1024 * 1024
|
190
|
+
when /([0-9]+)gb/i
|
191
|
+
bs = $1.to_i * 1024 * 1024 * 1024
|
192
|
+
else
|
193
|
+
bs = INFINITE
|
194
|
+
end
|
195
|
+
bs
|
196
|
+
end
|
197
|
+
|
198
|
+
# create the queues that are defined in the configuration
|
199
|
+
def setup_queue_manager
|
200
|
+
@log.info("[QueueManager] Create defined queues (#{@config["defined-queues"].size})")
|
201
|
+
for defined_queue in @config["defined-queues"]
|
202
|
+
create_queue_from_config(defined_queue[0], defined_queue[1])
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2008 Vincent Landgraf
|
3
|
+
#
|
4
|
+
# This file is part of the Free Message Queue.
|
5
|
+
#
|
6
|
+
# Foobar is free software: you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU General Public License as published by
|
8
|
+
# the Free Software Foundation, either version 3 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
#
|
11
|
+
# Foobar is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU General Public License
|
17
|
+
# along with Foobar. If not, see <http://www.gnu.org/licenses/>.
|
18
|
+
#
|
19
|
+
require 'ostruct'
|
20
|
+
|
21
|
+
module FreeMessageQueue
|
22
|
+
class AdminQueue
|
23
|
+
attr_accessor :manager
|
24
|
+
|
25
|
+
def initialize()
|
26
|
+
super
|
27
|
+
@filter_queues = []
|
28
|
+
end
|
29
|
+
|
30
|
+
def poll()
|
31
|
+
item = OpenStruct.new
|
32
|
+
|
33
|
+
queues_code = []
|
34
|
+
manager.queues.each do |queue_name|
|
35
|
+
# skip if it is filterd
|
36
|
+
next if filtered? queue_name
|
37
|
+
|
38
|
+
# add a new entry to the array
|
39
|
+
queues_code << queue_to_json(queue_name)
|
40
|
+
end
|
41
|
+
|
42
|
+
item.data = "[%s]" % queues_code.join(",")
|
43
|
+
item
|
44
|
+
end
|
45
|
+
|
46
|
+
def put(data)
|
47
|
+
if data.match(/_method=delete&path=(.*)/)
|
48
|
+
# delete queue
|
49
|
+
manager.delete_queue($1)
|
50
|
+
elsif data.match(/_method=create&data=(.*)/)
|
51
|
+
# create queue
|
52
|
+
conf = eval($1.gsub(":", "=>").gsub("null", "-1"))
|
53
|
+
manager.create_queue_from_config("dynamic-created-queue", conf)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def size
|
58
|
+
1 # there is always a message in the queue
|
59
|
+
end
|
60
|
+
|
61
|
+
def filter=(str)
|
62
|
+
@filter_queues = str.split " "
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
# check if the system queue should filter the queue
|
68
|
+
def filtered? (name)
|
69
|
+
skip = false
|
70
|
+
for filter in @filter_queues
|
71
|
+
skip = true if name[0...filter.size] == filter
|
72
|
+
end
|
73
|
+
skip
|
74
|
+
end
|
75
|
+
|
76
|
+
def queue_to_json(queue_name)
|
77
|
+
constraints = manager.queue_constraints(queue_name)
|
78
|
+
|
79
|
+
"[\"%s\", %d, %d, %d, %d]" % [
|
80
|
+
queue_name,
|
81
|
+
manager.queue(queue_name).bytes,
|
82
|
+
constraints[:max_size],
|
83
|
+
manager.queue(queue_name).size,
|
84
|
+
constraints[:max_messages],
|
85
|
+
]
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|