fmq 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|