stompserver_ng 1.0.6
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 +159 -0
- data/Manifest.txt +71 -0
- data/README.txt +172 -0
- data/Rakefile +38 -0
- data/STATUS +5 -0
- data/bin/stompserver_ng +63 -0
- data/client/README.txt +1 -0
- data/client/both.rb +25 -0
- data/client/consume.rb +14 -0
- data/client/send.rb +17 -0
- data/config/stompserver_ng.conf +11 -0
- data/etc/19xcompat/notes.txt +223 -0
- data/etc/arutils/README-activerecord.txt +78 -0
- data/etc/arutils/cre_mysql.rb +34 -0
- data/etc/arutils/cre_postgres.rb +33 -0
- data/etc/arutils/cre_sqlite3.rb +28 -0
- data/etc/arutils/mysql_boot.sql +12 -0
- data/etc/arutils/postgres_boot.sql +14 -0
- data/etc/database.mysql.yml +9 -0
- data/etc/database.postgres.yml +9 -0
- data/etc/passwd.example +3 -0
- data/etc/ppqinfo.rb +15 -0
- data/etc/runserver.sh +17 -0
- data/etc/stompserver_ng +50 -0
- data/etc/stompserver_ng.conf +13 -0
- data/lib/stomp_server_ng.rb +471 -0
- data/lib/stomp_server_ng/protocols/http.rb +128 -0
- data/lib/stomp_server_ng/protocols/stomp.rb +407 -0
- data/lib/stomp_server_ng/qmonitor.rb +58 -0
- data/lib/stomp_server_ng/queue.rb +248 -0
- data/lib/stomp_server_ng/queue/activerecord_queue.rb +118 -0
- data/lib/stomp_server_ng/queue/ar_message.rb +21 -0
- data/lib/stomp_server_ng/queue/ar_reconnect.rb +18 -0
- data/lib/stomp_server_ng/queue/dbm_queue.rb +72 -0
- data/lib/stomp_server_ng/queue/file_queue.rb +56 -0
- data/lib/stomp_server_ng/queue/memory_queue.rb +64 -0
- data/lib/stomp_server_ng/queue_manager.rb +302 -0
- data/lib/stomp_server_ng/stomp_auth.rb +26 -0
- data/lib/stomp_server_ng/stomp_frame.rb +32 -0
- data/lib/stomp_server_ng/stomp_frame_recognizer.rb +77 -0
- data/lib/stomp_server_ng/stomp_id.rb +32 -0
- data/lib/stomp_server_ng/stomp_user.rb +17 -0
- data/lib/stomp_server_ng/test_server.rb +21 -0
- data/lib/stomp_server_ng/topic_manager.rb +46 -0
- data/setup.rb +1585 -0
- data/stompserver_ng.gemspec +136 -0
- data/test/devserver/props.yaml +5 -0
- data/test/devserver/runserver.sh +16 -0
- data/test/devserver/stompserver_ng.dbm.conf +12 -0
- data/test/devserver/stompserver_ng.file.conf +12 -0
- data/test/devserver/stompserver_ng.memory.conf +12 -0
- data/test/noserver/mocklogger.rb +12 -0
- data/test/noserver/test_queue_manager.rb +134 -0
- data/test/noserver/test_stomp_frame.rb +138 -0
- data/test/noserver/test_topic_manager.rb +79 -0
- data/test/noserver/ts_all_no_server.rb +12 -0
- data/test/props.yaml +5 -0
- data/test/runalltests.sh +14 -0
- data/test/runtest.sh +4 -0
- data/test/test_0000_base.rb +107 -0
- data/test/test_0001_conn.rb +47 -0
- data/test/test_0002_conn_sr.rb +94 -0
- data/test/test_0006_client.rb +41 -0
- data/test/test_0011_send_recv.rb +74 -0
- data/test/test_0015_ack_conn.rb +78 -0
- data/test/test_0017_ack_client.rb +78 -0
- data/test/test_0019_ack_no_ack.rb +145 -0
- data/test/test_0022_ack_noack_conn.rb +123 -0
- data/test/test_0030_subscr_id.rb +44 -0
- data/test/test_0040_receipt_conn.rb +87 -0
- data/test/ts_all_server.rb +10 -0
- metadata +196 -0
@@ -0,0 +1,58 @@
|
|
1
|
+
#
|
2
|
+
#
|
3
|
+
#
|
4
|
+
module StompServer
|
5
|
+
#
|
6
|
+
# = Queue Monitor
|
7
|
+
#
|
8
|
+
class QueueMonitor
|
9
|
+
#
|
10
|
+
# Initialize the queue monitor.
|
11
|
+
#
|
12
|
+
def initialize(qstore,queues)
|
13
|
+
@qstore = qstore
|
14
|
+
@queues = queues
|
15
|
+
@stompid = StompServer::StompId.new
|
16
|
+
#
|
17
|
+
@@log = Logger.new(STDOUT)
|
18
|
+
@@log.level = StompServer::LogHelper.get_loglevel()
|
19
|
+
@@log.debug("QueueMonitor initialize comletes")
|
20
|
+
#
|
21
|
+
end
|
22
|
+
#
|
23
|
+
# Start monitor timer.
|
24
|
+
#
|
25
|
+
def start
|
26
|
+
count =0
|
27
|
+
EventMachine::add_periodic_timer 5, proc {count+=1; monitor(count) }
|
28
|
+
end
|
29
|
+
#
|
30
|
+
# Respond to calls from the timer. Do nothing if no clients are connected
|
31
|
+
# to the '/queue/monitor' destination.
|
32
|
+
#
|
33
|
+
def monitor(count)
|
34
|
+
return unless (@qstore.methods.include?(:monitor) | @qstore.methods.include?('monitor'))
|
35
|
+
users = @queues['/queue/monitor']
|
36
|
+
return if users.size == 0
|
37
|
+
stats = @qstore.monitor
|
38
|
+
return if stats.size == 0
|
39
|
+
body = ''
|
40
|
+
#
|
41
|
+
stats.each do |queue,qstats|
|
42
|
+
body << "Queue: #{queue}\n"
|
43
|
+
qstats.each {|stat,value| body << "#{stat}: #{value}\n"}
|
44
|
+
body << "\n"
|
45
|
+
end
|
46
|
+
#
|
47
|
+
headers = {
|
48
|
+
'message-id' => @stompid[count],
|
49
|
+
'destination' => '/queue/monitor',
|
50
|
+
'content-length' => body.size.to_s
|
51
|
+
}
|
52
|
+
#
|
53
|
+
frame = StompServer::StompFrame.new('MESSAGE', headers, body)
|
54
|
+
users.each {|user| user.connection.stomp_send_data(frame)}
|
55
|
+
end
|
56
|
+
end # of class QueueMonitor
|
57
|
+
end # of module StompServer
|
58
|
+
|
@@ -0,0 +1,248 @@
|
|
1
|
+
#
|
2
|
+
#
|
3
|
+
#
|
4
|
+
module StompServer
|
5
|
+
#
|
6
|
+
# == Queue
|
7
|
+
#
|
8
|
+
class Queue
|
9
|
+
# the check point interval
|
10
|
+
attr_accessor :checkpoint_interval
|
11
|
+
|
12
|
+
# initiialize
|
13
|
+
def initialize(directory='.stompserver', delete_empty=true)
|
14
|
+
|
15
|
+
@@log = Logger.new(STDOUT)
|
16
|
+
@@log.level = StompServer::LogHelper.get_loglevel()
|
17
|
+
@@log.debug("Q #{self} initialization starts")
|
18
|
+
|
19
|
+
@stompid = StompServer::StompId.new
|
20
|
+
@delete_empty = delete_empty
|
21
|
+
@directory = directory
|
22
|
+
Dir.mkdir(@directory) unless File.directory?(@directory)
|
23
|
+
if File.exists?("#{@directory}/qinfo")
|
24
|
+
qinfo = Hash.new
|
25
|
+
File.open("#{@directory}/qinfo", "rb") { |f| qinfo = Marshal.load(f.read)}
|
26
|
+
@queues = qinfo[:queues]
|
27
|
+
@frames = qinfo[:frames]
|
28
|
+
else
|
29
|
+
@queues = Hash.new
|
30
|
+
@frames = Hash.new
|
31
|
+
end
|
32
|
+
|
33
|
+
@queues.keys.each do |dest|
|
34
|
+
@@log.debug "Q #{self} dest=#{dest} size=#{@queues[dest][:size]} enqueued=#{@queues[dest][:enqueued]} dequeued=#{@queues[dest][:dequeued]}"
|
35
|
+
end
|
36
|
+
|
37
|
+
@@log.debug("Q #{self} initialized in #{@directory}")
|
38
|
+
|
39
|
+
#
|
40
|
+
# Cleanup dead queues and save the state of the queues every so often.
|
41
|
+
# Alternatively we could save the queue state every X number
|
42
|
+
# of frames that are put in the queue.
|
43
|
+
# Should probably also read it after saving it to confirm integrity.
|
44
|
+
#
|
45
|
+
# Removed: this badly corrupts the queue when stopping with messages
|
46
|
+
#
|
47
|
+
# EventMachine::add_periodic_timer 1800, proc {@queues.keys.each
|
48
|
+
# {|dest| close_queue(dest)};save_queue_state }
|
49
|
+
#
|
50
|
+
end
|
51
|
+
|
52
|
+
# stop
|
53
|
+
def stop(session_id)
|
54
|
+
@@log.debug "#{session_id} Shutting down Queues, queue count: #{@queues.size}"
|
55
|
+
#
|
56
|
+
@queues.keys.each do |dest|
|
57
|
+
@@log.debug "#{session_id}: Queue #{dest}: size=#{@queues[dest][:size]} enqueued=#{@queues[dest][:enqueued]} dequeued=#{@queues[dest][:dequeued]}"
|
58
|
+
close_queue(dest, session_id)
|
59
|
+
end
|
60
|
+
save_queue_state(session_id)
|
61
|
+
end
|
62
|
+
|
63
|
+
# save_queue_state
|
64
|
+
def save_queue_state(session_id)
|
65
|
+
@@log.debug "#{session_id} save_queue_state"
|
66
|
+
now=Time.now
|
67
|
+
@next_save ||=now
|
68
|
+
if now >= @next_save
|
69
|
+
@@log.debug "#{session_id} saving state"
|
70
|
+
qinfo = {:queues => @queues, :frames => @frames}
|
71
|
+
# write then rename to make sure this is atomic
|
72
|
+
File.open("#{@directory}/qinfo.new", "wb") { |f| f.write Marshal.dump(qinfo)}
|
73
|
+
File.rename("#{@directory}/qinfo.new","#{@directory}/qinfo")
|
74
|
+
@next_save=now+checkpoint_interval
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# monitor
|
79
|
+
def monitor
|
80
|
+
@@log.debug "#{self} monitor"
|
81
|
+
stats = Hash.new
|
82
|
+
@queues.keys.each do |dest|
|
83
|
+
stats[dest] = {
|
84
|
+
'size' => @queues[dest][:size],
|
85
|
+
'enqueued' => @queues[dest][:enqueued],
|
86
|
+
'dequeued' => @queues[dest][:dequeued],
|
87
|
+
'exceptions' => @queues[dest][:exceptions],
|
88
|
+
}
|
89
|
+
end
|
90
|
+
stats
|
91
|
+
end
|
92
|
+
|
93
|
+
# close_queue
|
94
|
+
def close_queue(dest, session_id)
|
95
|
+
@@log.debug "#{session_id} close_queue"
|
96
|
+
if @queues[dest][:size] == 0 and @queues[dest][:frames].size == 0 and @delete_empty
|
97
|
+
_close_queue(dest)
|
98
|
+
@queues.delete(dest)
|
99
|
+
@frames.delete(dest)
|
100
|
+
@@log.debug "#{session_id} Queue #{dest} removed."
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# open_queue
|
105
|
+
def open_queue(dest, session_id)
|
106
|
+
@@log.debug "#{session_id} open_queue"
|
107
|
+
# New queue
|
108
|
+
@queues[dest] = Hash.new
|
109
|
+
# New frames for this queue
|
110
|
+
@frames[dest] = Hash.new
|
111
|
+
# Update queues
|
112
|
+
# :size, :frames, :msgid, :enqueued, :dequeued, :exceptions
|
113
|
+
@queues[dest][:size] = 0
|
114
|
+
@queues[dest][:frames] = Array.new
|
115
|
+
@queues[dest][:msgid] = 1
|
116
|
+
@queues[dest][:enqueued] = 0
|
117
|
+
@queues[dest][:dequeued] = 0
|
118
|
+
@queues[dest][:exceptions] = 0
|
119
|
+
_open_queue(dest)
|
120
|
+
@@log.debug "Created queue #{dest}"
|
121
|
+
end
|
122
|
+
|
123
|
+
# requeue
|
124
|
+
def requeue(dest,frame)
|
125
|
+
@@log.debug "#{frame.headers['session']} requeue, for #{dest}, frame: #{frame.inspect}"
|
126
|
+
open_queue(dest, frame.headers['session']) unless @queues.has_key?(dest)
|
127
|
+
msgid = frame.headers['message-id']
|
128
|
+
#
|
129
|
+
# Note: frame.headers['max-exceptions'] is currently _never_ set any where!
|
130
|
+
#
|
131
|
+
if frame.headers['max-exceptions'] and @frames[dest][msgid][:exceptions] >= frame.headers['max-exceptions'].to_i
|
132
|
+
enqueue("/queue/deadletter",frame)
|
133
|
+
return
|
134
|
+
end
|
135
|
+
#
|
136
|
+
writeframe(dest,frame,msgid)
|
137
|
+
|
138
|
+
# update queues (queues[dest])
|
139
|
+
# :size, :frames, :msgid, :enqueued, :dequeued, :exceptions
|
140
|
+
@queues[dest][:size] += 1
|
141
|
+
@queues[dest][:frames].unshift(msgid)
|
142
|
+
# no :msgid here
|
143
|
+
# no :enqueued here
|
144
|
+
# no :dequeued here
|
145
|
+
@queues[dest][:exceptions] += 1
|
146
|
+
|
147
|
+
# update frames
|
148
|
+
#
|
149
|
+
# Is this _always_ the case in this method ?????
|
150
|
+
unless @frames[dest][msgid]
|
151
|
+
new_frames_entry(dest, frame, msgid)
|
152
|
+
end
|
153
|
+
#
|
154
|
+
@frames[dest][msgid][:exceptions] += 1
|
155
|
+
@frames[dest][msgid][:requeued] += 1
|
156
|
+
save_queue_state(frame.headers['session'])
|
157
|
+
return true
|
158
|
+
end
|
159
|
+
|
160
|
+
# enqueue
|
161
|
+
def enqueue(dest,frame)
|
162
|
+
@@log.debug "#{frame.headers['session']} enqueue"
|
163
|
+
open_queue(dest, frame.headers['session']) unless @queues.has_key?(dest)
|
164
|
+
msgid = assign_id(frame, dest)
|
165
|
+
@@log.debug("#{frame.headers['session']} Enqueue for message: #{msgid} Client: #{frame.headers['client-id'] if frame.headers['client-id']}")
|
166
|
+
writeframe(dest,frame,msgid)
|
167
|
+
|
168
|
+
# update queues (queues[dest])
|
169
|
+
# :size, :frames, :msgid, :enqueued, :dequeued, :exceptions
|
170
|
+
@queues[dest][:size] += 1
|
171
|
+
@queues[dest][:frames].push(msgid)
|
172
|
+
@queues[dest][:msgid] += 1
|
173
|
+
@queues[dest][:enqueued] += 1
|
174
|
+
# no :dequeue here
|
175
|
+
# no :exceptions here
|
176
|
+
|
177
|
+
# Update frames
|
178
|
+
# Initialize frames entry for this: dest, frame, and msgid
|
179
|
+
new_frames_entry(dest, frame, msgid)
|
180
|
+
save_queue_state(frame.headers['session'])
|
181
|
+
return true
|
182
|
+
end
|
183
|
+
|
184
|
+
# dequeue
|
185
|
+
def dequeue(dest, session_id)
|
186
|
+
@@log.debug "#{session_id} dequeue, dest: #{dest}"
|
187
|
+
return false unless message_for?(dest, session_id)
|
188
|
+
# update queues ... dest .... :frames here
|
189
|
+
msgid = @queues[dest][:frames].shift
|
190
|
+
frame = readframe(dest,msgid,session_id)
|
191
|
+
@@log.debug("#{frame.headers['session']} Dequeue for message: #{msgid} Client: #{frame.headers['client-id'] if frame.headers['client-id']}")
|
192
|
+
|
193
|
+
# update queues (queues[dest])
|
194
|
+
# :size, :frames, :msgid, :enqueued, :dequeued, :exceptions
|
195
|
+
@queues[dest][:size] -= 1
|
196
|
+
# :frames - see above
|
197
|
+
@queues[dest][:msgid] -= 1
|
198
|
+
# :enqueued - no change
|
199
|
+
@queues[dest][:dequeued] += 1
|
200
|
+
# :exceptions - no change
|
201
|
+
|
202
|
+
@queues[dest].delete(msgid)
|
203
|
+
|
204
|
+
close_queue(dest, frame.headers['session'])
|
205
|
+
save_queue_state(frame.headers['session'])
|
206
|
+
return frame
|
207
|
+
end
|
208
|
+
|
209
|
+
# messsage_for?
|
210
|
+
def message_for?(dest, session_id)
|
211
|
+
retval = (@queues.has_key?(dest) and (!@queues[dest][:frames].empty?))
|
212
|
+
@@log.debug "#{session_id} message_for?, dest: #{dest}, #{retval}"
|
213
|
+
return retval
|
214
|
+
end
|
215
|
+
|
216
|
+
# writeframe
|
217
|
+
def writeframe(dest,frame,msgid)
|
218
|
+
@@log.debug "#{frame.headers['session']} writeframe, dest: #{dest}, frame: #{frame}, msgid: #{msgid}"
|
219
|
+
_writeframe(dest,frame,msgid)
|
220
|
+
end
|
221
|
+
|
222
|
+
# readframe
|
223
|
+
def readframe(dest,msgid, session_id)
|
224
|
+
@@log.debug "#{session_id} readframe, dest: #{dest}, msgid: #{msgid}"
|
225
|
+
_readframe(dest,msgid)
|
226
|
+
end
|
227
|
+
|
228
|
+
# assign_id
|
229
|
+
def assign_id(frame, dest)
|
230
|
+
@@log.debug "#{frame.headers['session']} assign_id, frame: #{frame}, dest: #{dest}"
|
231
|
+
msg_id = @queues[dest].nil? ? 1 : @queues[dest][:msgid]
|
232
|
+
frame.headers['message-id'] = @stompid[msg_id]
|
233
|
+
end
|
234
|
+
|
235
|
+
private
|
236
|
+
# new_frames_entry
|
237
|
+
def new_frames_entry(dest, frame, msgid)
|
238
|
+
@frames[dest][msgid] = Hash.new
|
239
|
+
@frames[dest][msgid][:exceptions] = 0
|
240
|
+
@frames[dest][msgid][:requeued] = 0
|
241
|
+
@frames[dest][msgid][:client_id] = frame.headers['client-id'] if frame.headers['client-id']
|
242
|
+
@frames[dest][msgid][:expires] = frame.headers['expires'] if frame.headers['expires']
|
243
|
+
end
|
244
|
+
#
|
245
|
+
end # class Queue
|
246
|
+
#
|
247
|
+
end # module StompServer
|
248
|
+
|
@@ -0,0 +1,118 @@
|
|
1
|
+
## Queue implementation using ActiveRecord
|
2
|
+
##
|
3
|
+
## all messages are stored in a single table
|
4
|
+
## they are indexed by 'stomp_id' which is the stomp 'message-id' header
|
5
|
+
## which must be unique accross all queues
|
6
|
+
##
|
7
|
+
require 'stomp_server_ng/queue/ar_message'
|
8
|
+
require 'yaml'
|
9
|
+
|
10
|
+
module StompServer
|
11
|
+
class ActiveRecordQueue
|
12
|
+
attr_accessor :checkpoint_interval
|
13
|
+
|
14
|
+
def initialize(configdir, storagedir, db_ymlfile)
|
15
|
+
# Default configuration, use SQLite for simplicity
|
16
|
+
db_params = {
|
17
|
+
'adapter' => 'sqlite3',
|
18
|
+
'database' => "#{configdir}/stompserver_development"
|
19
|
+
}
|
20
|
+
@@log = Logger::new(STDOUT)
|
21
|
+
@@log.level = StompServer::LogHelper.get_loglevel()
|
22
|
+
# Load DB configuration
|
23
|
+
@@log.debug "trying to read from #{db_ymlfile}"
|
24
|
+
if File.exists? db_ymlfile
|
25
|
+
@@log.debug("File #{db_ymlfile} exists.")
|
26
|
+
db_params.merge! YAML::load(File.open(db_ymlfile))
|
27
|
+
else
|
28
|
+
@@log.warn("File #{db_ymlfile} not found, using sqlite3 default.")
|
29
|
+
end
|
30
|
+
@@log.debug("using DB params: #{db_params.inspect}")
|
31
|
+
# Setup activerecord
|
32
|
+
ActiveRecord::Base.establish_connection(db_params)
|
33
|
+
@@log.debug("connection complete")
|
34
|
+
|
35
|
+
# AR Logger
|
36
|
+
ActiveRecord::Base.logger = Logger.new(STDOUT)
|
37
|
+
ActiveRecord::Base.logger.level = StompServer::LogHelper.get_loglevel()
|
38
|
+
|
39
|
+
# we need the connection, it can't be done earlier
|
40
|
+
ArMessage.reset_column_information
|
41
|
+
reload_queues
|
42
|
+
@stompid = StompServer::StompId.new
|
43
|
+
end
|
44
|
+
|
45
|
+
# Add a frame to the queue
|
46
|
+
def enqueue(queue_name, frame)
|
47
|
+
unless @frames[queue_name]
|
48
|
+
@frames[queue_name] = {
|
49
|
+
:last_index => 0,
|
50
|
+
:frames => [],
|
51
|
+
}
|
52
|
+
end
|
53
|
+
affect_msgid_and_store(frame, queue_name)
|
54
|
+
@frames[queue_name][:frames] << frame
|
55
|
+
end
|
56
|
+
|
57
|
+
# Get and remove a frame from the queue
|
58
|
+
def dequeue(queue_name, session_id)
|
59
|
+
return nil unless @frames[queue_name] && !@frames[queue_name][:frames].empty?
|
60
|
+
frame = @frames[queue_name][:frames].shift
|
61
|
+
remove_from_store(frame.headers['message-id'])
|
62
|
+
return frame
|
63
|
+
end
|
64
|
+
|
65
|
+
# Requeue the frame previously pending
|
66
|
+
def requeue(queue_name, frame)
|
67
|
+
@frames[queue_name][:frames] << frame
|
68
|
+
ArMessage.create!(:stomp_id => frame.headers['message-id'],
|
69
|
+
:frame => frame)
|
70
|
+
end
|
71
|
+
|
72
|
+
# remove a frame from the store
|
73
|
+
def remove_from_store(message_id)
|
74
|
+
ArMessage.find_by_stomp_id(message_id).destroy
|
75
|
+
end
|
76
|
+
|
77
|
+
# store a frame (assigning it a message-id)
|
78
|
+
def affect_msgid_and_store(frame, queue_name)
|
79
|
+
msgid = assign_id(frame, queue_name)
|
80
|
+
ArMessage.create!(:stomp_id => msgid, :frame => frame)
|
81
|
+
end
|
82
|
+
|
83
|
+
def message_for?(queue_name, session_id)
|
84
|
+
@frames[queue_name] && !@frames[queue_name][:frames].empty?
|
85
|
+
end
|
86
|
+
|
87
|
+
def assign_id(frame, queue_name)
|
88
|
+
|
89
|
+
unless @frames[queue_name]
|
90
|
+
@frames[queue_name] = {
|
91
|
+
:last_index => 0,
|
92
|
+
:frames => [],
|
93
|
+
}
|
94
|
+
end
|
95
|
+
|
96
|
+
msgid = @stompid[@frames[queue_name][:last_index] += 1]
|
97
|
+
frame.headers['message-id'] = msgid
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
def reload_queues
|
102
|
+
@frames = Hash.new
|
103
|
+
ArMessage.find(:all).each { |message|
|
104
|
+
frame = message.frame
|
105
|
+
destination = frame.dest
|
106
|
+
msgid = message.stomp_id
|
107
|
+
@frames[destination] ||= Hash.new
|
108
|
+
@frames[destination][:frames] ||= Array.new
|
109
|
+
@frames[destination][:frames] << frame
|
110
|
+
}
|
111
|
+
# compute base index for each destination
|
112
|
+
@frames.each_pair { |destination,hash|
|
113
|
+
hash[:last_index] = hash[:frames].map{|f|
|
114
|
+
f.headers['message-id'].match(/(\d+)\Z/)[0].to_i}.max
|
115
|
+
}
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
gem 'activerecord'
|
2
|
+
require 'active_record'
|
3
|
+
require 'stomp_server_ng/queue/ar_reconnect'
|
4
|
+
#
|
5
|
+
#
|
6
|
+
#
|
7
|
+
class Logger
|
8
|
+
private
|
9
|
+
# Rails overrides this method so that it can customize the format
|
10
|
+
# of it's logs. A consequence it that the date, time, etc. disappear
|
11
|
+
# from log output. This little hack overrides the override :-).
|
12
|
+
def format_message(*args)
|
13
|
+
old_format_message(*args)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
#
|
17
|
+
#
|
18
|
+
#
|
19
|
+
class ArMessage < ActiveRecord::Base
|
20
|
+
serialize :frame
|
21
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'active_record/connection_adapters/mysql_adapter'
|
2
|
+
module ActiveRecord::ConnectionAdapters
|
3
|
+
class MysqlAdapter
|
4
|
+
alias_method :execute_without_retry, :execute
|
5
|
+
def execute(*args)
|
6
|
+
execute_without_retry(*args)
|
7
|
+
rescue ActiveRecord::StatementInvalid
|
8
|
+
if $!.message =~ /server has gone away/i
|
9
|
+
warn "Server timed out, retrying"
|
10
|
+
reconnect!
|
11
|
+
retry
|
12
|
+
end
|
13
|
+
|
14
|
+
raise
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|