brown 1.1.2 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +4 -1
- data/.yardopts +1 -0
- data/LICENCE +674 -0
- data/README.md +321 -0
- data/bin/brown +72 -12
- data/brown.gemspec +31 -21
- data/lib/.gitkeep +0 -0
- data/lib/brown.rb +17 -3
- data/lib/brown/agent.rb +417 -21
- data/lib/brown/agent/amqp_message.rb +42 -0
- data/lib/brown/agent/amqp_message_mock.rb +28 -0
- data/lib/brown/agent/amqp_publisher.rb +169 -0
- data/lib/brown/agent/memo.rb +64 -0
- data/lib/brown/agent/stimulus.rb +143 -0
- data/lib/brown/test.rb +152 -0
- metadata +88 -64
- data/Gemfile +0 -3
- data/Rakefile +0 -19
- data/lib/brown/acl_loader.rb +0 -57
- data/lib/brown/acl_lookup.rb +0 -52
- data/lib/brown/amqp_errors.rb +0 -148
- data/lib/brown/logger.rb +0 -51
- data/lib/brown/message.rb +0 -73
- data/lib/brown/module_methods.rb +0 -134
- data/lib/brown/queue_definition.rb +0 -32
- data/lib/brown/queue_factory.rb +0 -33
- data/lib/brown/receiver.rb +0 -143
- data/lib/brown/sender.rb +0 -92
- data/lib/brown/util.rb +0 -58
- data/lib/smith.rb +0 -4
data/lib/brown/acl_loader.rb
DELETED
@@ -1,57 +0,0 @@
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
2
|
-
|
3
|
-
require 'tmpdir'
|
4
|
-
require 'protobuf'
|
5
|
-
|
6
|
-
require "brown/logger"
|
7
|
-
|
8
|
-
class Brown::ACLLoader
|
9
|
-
extend Brown::Logger
|
10
|
-
|
11
|
-
def self.load_all(*dirs)
|
12
|
-
dirs.flatten!
|
13
|
-
pfiles = dirs.each_with_object([]) do |dir, list|
|
14
|
-
list << Dir["#{dir}/*.proto"]
|
15
|
-
end.flatten
|
16
|
-
|
17
|
-
load_proto_files(pfiles, dirs)
|
18
|
-
end
|
19
|
-
|
20
|
-
def self.load_proto_files(pfiles, dirs)
|
21
|
-
orig_load_path = $LOAD_PATH
|
22
|
-
|
23
|
-
Dir.mktmpdir do |tmpdir|
|
24
|
-
dirs = pfiles.map { |f| File.dirname(f) }.uniq + [tmpdir]
|
25
|
-
dirs.each { |d| $LOAD_PATH.unshift(d) }
|
26
|
-
|
27
|
-
compiles = []
|
28
|
-
|
29
|
-
pfiles.each do |f|
|
30
|
-
pbrbfile = f.gsub(/\.proto$/, ".pb.rb")
|
31
|
-
unless File.exists?(pbrbfile) and File.stat(pfile).mtime <= File.stat(pbrbfile).mtime
|
32
|
-
compiles << f
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
includes = dirs.map { |d| "-I '#{d}'" }.join(" ")
|
37
|
-
|
38
|
-
unless compiles.empty?
|
39
|
-
cmd = "protoc --ruby_out='#{tmpdir}' #{includes} #{compiles.map { |f| "'#{f}'" }.join(' ')} 2>&1"
|
40
|
-
output = nil
|
41
|
-
|
42
|
-
IO.popen(cmd) { |fd| output = fd.read }
|
43
|
-
|
44
|
-
if $?.exitstatus != 0
|
45
|
-
logger.fatal { "protoc failed: #{output}" }
|
46
|
-
raise RuntimeError, output
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
pfiles.each do |f|
|
51
|
-
require "#{File.basename(f, '.proto')}.pb"
|
52
|
-
end
|
53
|
-
end
|
54
|
-
ensure
|
55
|
-
$LOAD_PATH.replace(orig_load_path)
|
56
|
-
end
|
57
|
-
end
|
data/lib/brown/acl_lookup.rb
DELETED
@@ -1,52 +0,0 @@
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
2
|
-
|
3
|
-
require 'extlib'
|
4
|
-
require 'murmurhash3'
|
5
|
-
|
6
|
-
module Brown::ACLLookup
|
7
|
-
def get_by_hash(type)
|
8
|
-
hashes[type]
|
9
|
-
end
|
10
|
-
module_function :get_by_hash
|
11
|
-
|
12
|
-
def get_by_type(type)
|
13
|
-
to_murmur32(type)
|
14
|
-
end
|
15
|
-
module_function :get_by_type
|
16
|
-
|
17
|
-
# Look the key up in the cache. This defaults to the key being the hash.
|
18
|
-
# If :by_type => true is passed in as the second argument then it will
|
19
|
-
# perform the lookup in the type hash.
|
20
|
-
#
|
21
|
-
def include?(key, opts={})
|
22
|
-
if opts[:by_type]
|
23
|
-
!get_by_type(key).nil?
|
24
|
-
else
|
25
|
-
!get_by_hash(key).nil?
|
26
|
-
end
|
27
|
-
end
|
28
|
-
module_function :include?
|
29
|
-
|
30
|
-
def clear!
|
31
|
-
@hashes = nil
|
32
|
-
end
|
33
|
-
module_function :clear!
|
34
|
-
|
35
|
-
def hashes
|
36
|
-
@hashes ||= begin
|
37
|
-
map = ObjectSpace.each_object(Class).map do |k|
|
38
|
-
[[to_murmur32(k), k], [k.to_s.split(/::/).last.snake_case, k]]
|
39
|
-
end.flatten(1)
|
40
|
-
Hash[map]
|
41
|
-
end
|
42
|
-
end
|
43
|
-
module_function :hashes
|
44
|
-
|
45
|
-
private
|
46
|
-
|
47
|
-
# Convert the name to a base 36 murmur hash
|
48
|
-
def to_murmur32(type)
|
49
|
-
MurmurHash3::V32.murmur3_32_str_hash(type.to_s).to_s(36)
|
50
|
-
end
|
51
|
-
module_function :to_murmur32
|
52
|
-
end
|
data/lib/brown/amqp_errors.rb
DELETED
@@ -1,148 +0,0 @@
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
2
|
-
|
3
|
-
module Brown::AmqpErrors
|
4
|
-
def error_message(code, text, &blk)
|
5
|
-
details = error_lookup(code)
|
6
|
-
message = "#{details[:error_class]} exception: #{code} " +
|
7
|
-
"(#{details[:name]}). #{details[:description]}"
|
8
|
-
|
9
|
-
case code
|
10
|
-
when 404
|
11
|
-
diagnosis = "looks like the queue has been deleted."
|
12
|
-
when 406
|
13
|
-
case text
|
14
|
-
when /.*(unknown delivery tag [0-9]+).*/
|
15
|
-
diagnosis = "#{$1} - you've probably already acknowledged the message."
|
16
|
-
end
|
17
|
-
else
|
18
|
-
end
|
19
|
-
blk.call(message, diagnosis)
|
20
|
-
end
|
21
|
-
|
22
|
-
def error_lookup(code)
|
23
|
-
errors[code]
|
24
|
-
end
|
25
|
-
|
26
|
-
def errors
|
27
|
-
@errors ||= {
|
28
|
-
311 => {
|
29
|
-
:name => "content-too-large",
|
30
|
-
:error_class => "Channel",
|
31
|
-
:description => "The client attempted to transfer content larger " +
|
32
|
-
"than the server could accept at the present time. " +
|
33
|
-
"The client may retry at a later time."
|
34
|
-
},
|
35
|
-
313 => {
|
36
|
-
:name => "no-consumers",
|
37
|
-
:error_class => "Channel",
|
38
|
-
:description => "When the exchange cannot deliver to a consumer " +
|
39
|
-
"when the immediate flag is set. As a result of " +
|
40
|
-
"pending data on the queue or the absence of any " +
|
41
|
-
"consumers of the queue."
|
42
|
-
},
|
43
|
-
320 => {
|
44
|
-
:name => "connection-forced",
|
45
|
-
:error_class => "Connection",
|
46
|
-
:description => "An operator intervened to close the Connection " +
|
47
|
-
"for some reason. The client may retry at some " +
|
48
|
-
"later date."
|
49
|
-
},
|
50
|
-
402 => {
|
51
|
-
:name => "invalid-path",
|
52
|
-
:error_class => "Connection",
|
53
|
-
:description => "The client tried to work with an unknown virtual host."
|
54
|
-
},
|
55
|
-
403 => {
|
56
|
-
:name => "access-refused",
|
57
|
-
:error_class => "Channel",
|
58
|
-
:description => "The client attempted to work with a server " +
|
59
|
-
"entity to which it has no access due to security " +
|
60
|
-
"settings."
|
61
|
-
},
|
62
|
-
404 => {
|
63
|
-
:name => "not-found",
|
64
|
-
:error_class => "Channel",
|
65
|
-
:description => "The client attempted to work with a server " +
|
66
|
-
"entity that does not exist."
|
67
|
-
},
|
68
|
-
405 => {
|
69
|
-
:name => "resource-locked",
|
70
|
-
:error_class => "Channel",
|
71
|
-
:description => "The client attempted to work with a server " +
|
72
|
-
"entity to which it has no access because " +
|
73
|
-
"another client is working with it."
|
74
|
-
},
|
75
|
-
406 => {
|
76
|
-
:name => "precondition-failed",
|
77
|
-
:error_class => "Channel",
|
78
|
-
:description => "The client requested a method that was not " +
|
79
|
-
"allowed because some precondition failed."
|
80
|
-
},
|
81
|
-
501 => {
|
82
|
-
:name => "frame-error",
|
83
|
-
:error_class => "Connection",
|
84
|
-
:description => "The sender sent a malformed frame that the " +
|
85
|
-
"recipient could not decode. This strongly " +
|
86
|
-
"implies a programming error in the sending peer."
|
87
|
-
},
|
88
|
-
502 => {
|
89
|
-
:name => "syntax-error",
|
90
|
-
:error_class => "Connection",
|
91
|
-
:description => "The sender sent a frame that contained illegal " +
|
92
|
-
"values for one or more fields. This strongly " +
|
93
|
-
"implies a programming error in the sending peer."
|
94
|
-
},
|
95
|
-
503 => {
|
96
|
-
:name => "command-invalid",
|
97
|
-
:error_class => "Connection",
|
98
|
-
:description => "The client sent an invalid sequence of frames, " +
|
99
|
-
"attempting to perform an operation that was " +
|
100
|
-
"considered invalid by the server. This usually " +
|
101
|
-
"implies a programming error in the client."
|
102
|
-
},
|
103
|
-
504 => {
|
104
|
-
:name => "channel-error",
|
105
|
-
:error_class => "Connection",
|
106
|
-
:description => "The client attempted to work with a Channel that " +
|
107
|
-
"had not been correctly opened. This most likely " +
|
108
|
-
"indicates a fault in the client layer."
|
109
|
-
},
|
110
|
-
505 => {
|
111
|
-
:name => "unexpected-frame",
|
112
|
-
:error_class => "Connection",
|
113
|
-
:description => "The peer sent a frame that was not expected, " +
|
114
|
-
"usually in the context of a content header and " +
|
115
|
-
"body. This strongly indicates a fault in the " +
|
116
|
-
"peer's content processing."
|
117
|
-
},
|
118
|
-
506 => {
|
119
|
-
:name => "resource-error",
|
120
|
-
:error_class => "Connection",
|
121
|
-
:description => "The server could not complete the method because " +
|
122
|
-
"it lacked sufficient resources. This may be due " +
|
123
|
-
"to the client creating too many of some type of entity."
|
124
|
-
},
|
125
|
-
530 => {
|
126
|
-
:name => "not-allowed",
|
127
|
-
:error_class => "Connection",
|
128
|
-
:description => "The client tried to work with some entity in a " +
|
129
|
-
"manner that is prohibited by the server, due to " +
|
130
|
-
"security settings or by some other criteria."
|
131
|
-
},
|
132
|
-
540 => {
|
133
|
-
:name => "not-implemented",
|
134
|
-
:error_class => "Connection",
|
135
|
-
:description => "The client tried to use functionality that is " +
|
136
|
-
"not implemented in the server."
|
137
|
-
},
|
138
|
-
541 => {
|
139
|
-
:name => "internal-error",
|
140
|
-
:error_class => "Connection",
|
141
|
-
:description => "The server could not complete the method " +
|
142
|
-
"because of an internal error. The server may " +
|
143
|
-
"require intervention by an operator in order " +
|
144
|
-
"to resume normal operations."
|
145
|
-
}
|
146
|
-
}
|
147
|
-
end
|
148
|
-
end
|
data/lib/brown/logger.rb
DELETED
@@ -1,51 +0,0 @@
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
2
|
-
|
3
|
-
require 'logger'
|
4
|
-
|
5
|
-
class Logger
|
6
|
-
VERBOSE = 0.5
|
7
|
-
TRACE = -1
|
8
|
-
|
9
|
-
# Lack of prior planning, peeps!
|
10
|
-
remove_const(:SEV_LABEL)
|
11
|
-
SEV_LABEL = {
|
12
|
-
TRACE => "TRACE",
|
13
|
-
DEBUG => "DEBUG",
|
14
|
-
VERBOSE => "VERB",
|
15
|
-
INFO => "INFO",
|
16
|
-
WARN => "WARN",
|
17
|
-
ERROR => "ERROR",
|
18
|
-
FATAL => "FATAL"
|
19
|
-
}
|
20
|
-
|
21
|
-
def verbose(progname = nil, &block)
|
22
|
-
add(VERBOSE, nil, progname, &block)
|
23
|
-
end
|
24
|
-
|
25
|
-
def trace(progname = nil, &block)
|
26
|
-
add(TRACE, nil, progname, &block)
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
module Brown::Logger
|
31
|
-
def logger
|
32
|
-
@logger ||= begin
|
33
|
-
Logger.new($stderr).tap do |l|
|
34
|
-
l.formatter = proc { |s,dt,n,msg| "#{$$} [#{s[0]}] #{msg}\n" }
|
35
|
-
l.level = Logger.const_get(Brown.log_level.upcase.to_sym)
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
def log_level(level=nil)
|
41
|
-
if level
|
42
|
-
logger.level = Logger.const_get(level.upcase.to_sym)
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
def backtrace(ex)
|
47
|
-
if ex.respond_to?(:backtrace) and ex.backtrace
|
48
|
-
self.debug { ex.backtrace.map { |l| " #{l}" }.join("\n") }
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
data/lib/brown/message.rb
DELETED
@@ -1,73 +0,0 @@
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
2
|
-
|
3
|
-
require 'brown/logger'
|
4
|
-
|
5
|
-
class Brown::Message
|
6
|
-
include Brown::Logger
|
7
|
-
|
8
|
-
attr_reader :payload, :metadata
|
9
|
-
|
10
|
-
def initialize(payload, metadata, requeue_queue, requeue_options, opts = {}, &blk)
|
11
|
-
@metadata = metadata
|
12
|
-
|
13
|
-
@requeue_queue = requeue_queue
|
14
|
-
@requeue_options = requeue_options
|
15
|
-
|
16
|
-
@requeue_options[:strategy] ||= :linear
|
17
|
-
|
18
|
-
@requeue_options[:on_requeue] ||= ->(count, total_count, cumulative_delay) {
|
19
|
-
logger.info { "Requeuing (#{@requeue_options[:strategy]}) message on queue: #{@requeue_queue.name}, count: #{count} of #{total_count}." }
|
20
|
-
}
|
21
|
-
|
22
|
-
@requeue_options[:on_requeue_limit] ||= ->(message, count, total_count, cumulative_delay) {
|
23
|
-
logger.info { "Not attempting any more requeues, requeue limit reached: #{total_count} for queue: #{@requeue_queue.name}, cummulative delay: #{cumulative_delay}s." }
|
24
|
-
}
|
25
|
-
|
26
|
-
klass = Brown::ACLLookup.get_by_hash(metadata.type)
|
27
|
-
raise RuntimeError, "Unknown ACL: #{metadata.type}" if klass.nil?
|
28
|
-
|
29
|
-
@payload = klass.new.parse_from_string(payload)
|
30
|
-
|
31
|
-
blk.call(@payload, self)
|
32
|
-
ack if opts[:auto_ack]
|
33
|
-
end
|
34
|
-
|
35
|
-
def ack(multiple = false)
|
36
|
-
@metadata.ack(multiple)
|
37
|
-
end
|
38
|
-
|
39
|
-
def nak(opts = {})
|
40
|
-
@metadata.reject(opts)
|
41
|
-
end
|
42
|
-
|
43
|
-
alias_method :reject, :nak
|
44
|
-
|
45
|
-
def requeue
|
46
|
-
if current_requeue_number < @requeue_options[:count]
|
47
|
-
cumulative_delay = case @requeue_options[:strategy].to_sym
|
48
|
-
when :linear
|
49
|
-
@requeue_options[:delay] * (current_requeue_number + 1)
|
50
|
-
when :exponential
|
51
|
-
@requeue_options[:delay] * (2 ** current_requeue_number)
|
52
|
-
when :exponential_no_initial_delay
|
53
|
-
@requeue_options[:delay] * (2 ** current_requeue_number - 1)
|
54
|
-
else
|
55
|
-
raise RuntimeError, "Unknown requeue strategy #{@requeue_options[:strategy].to_sym.inspect}"
|
56
|
-
end
|
57
|
-
|
58
|
-
EM.add_timer(cumulative_delay) do
|
59
|
-
new_headers = (@metadata.headers || {}).merge('requeue' => current_requeue_number + 1)
|
60
|
-
@requeue_queue.publish(@payload, @metadata.to_hash.merge(:headers => new_headers))
|
61
|
-
end
|
62
|
-
|
63
|
-
@requeue_options[:on_requeue].call(current_requeue_number + 1, @requeue_options[:count], cumulative_delay)
|
64
|
-
else
|
65
|
-
@requeue_options[:on_requeue_limit].call(@payload, current_requeue_number + 1, @requeue_options[:count], @requeue_options[:delay] * current_requeue_number)
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
private
|
70
|
-
def current_requeue_number
|
71
|
-
(@metadata.headers['requeue'] rescue nil) || 0
|
72
|
-
end
|
73
|
-
end
|
data/lib/brown/module_methods.rb
DELETED
@@ -1,134 +0,0 @@
|
|
1
|
-
# Remove any pre-existing activation of eventmachine, so that `eventmachine-le`
|
2
|
-
# takes priority
|
3
|
-
$:.delete_if { |d| d =~ /\/eventmachine-\d/ }
|
4
|
-
|
5
|
-
require 'eventmachine-le'
|
6
|
-
require 'amqp'
|
7
|
-
require 'uri'
|
8
|
-
|
9
|
-
require 'brown/logger'
|
10
|
-
|
11
|
-
module Brown::ModuleMethods
|
12
|
-
include Brown::Logger
|
13
|
-
|
14
|
-
attr_reader :connection, :log_level
|
15
|
-
|
16
|
-
def compile_acls
|
17
|
-
end
|
18
|
-
|
19
|
-
def config
|
20
|
-
Class.new.tap do |cfg|
|
21
|
-
cfg.send(:define_method, :method_missing) do |*_args|
|
22
|
-
Class.new.tap do |group|
|
23
|
-
group.send(:define_method, :method_missing) do |*_args|
|
24
|
-
""
|
25
|
-
end
|
26
|
-
end.new
|
27
|
-
end
|
28
|
-
end.new
|
29
|
-
end
|
30
|
-
|
31
|
-
def running?
|
32
|
-
EM.reactor_running?
|
33
|
-
end
|
34
|
-
|
35
|
-
def start(opts={})
|
36
|
-
@log_level = opts[:log_level] || "info"
|
37
|
-
|
38
|
-
# Why these are not the defaults, I will never know
|
39
|
-
EM.epoll if EM.epoll?
|
40
|
-
EM.kqueue if EM.kqueue?
|
41
|
-
|
42
|
-
connection_settings = {
|
43
|
-
:on_tcp_connection_failure => method(:tcp_connection_failure_handler),
|
44
|
-
:on_possible_authentication_failure => method(:authentication_failure_handler)
|
45
|
-
}
|
46
|
-
|
47
|
-
AMQP.start(opts[:server_url], connection_settings) do |connection|
|
48
|
-
EM.threadpool_size = 1
|
49
|
-
@connection = connection
|
50
|
-
|
51
|
-
connection.on_connection do
|
52
|
-
logger.info { "Connected to: AMQP Broker: #{broker_identifier(connection)}" }
|
53
|
-
end
|
54
|
-
|
55
|
-
connection.on_tcp_connection_loss do |connection, settings|
|
56
|
-
logger.info { "Reconnecting to AMQP Broker: #{broker_identifier(connection)} in 5s" }
|
57
|
-
connection.reconnect(false, 5)
|
58
|
-
end
|
59
|
-
|
60
|
-
connection.after_recovery do |connection|
|
61
|
-
logger.info { "Connection with AMQP Broker restored: #{broker_identifier(connection)}" }
|
62
|
-
end
|
63
|
-
|
64
|
-
connection.on_error do |connection, connection_close|
|
65
|
-
# If the broker is gracefully shutdown we get a 320. Log a nice message.
|
66
|
-
if connection_close.reply_code == 320
|
67
|
-
logger.info { "AMQP Broker shutdown: #{broker_identifier(connection)}" }
|
68
|
-
else
|
69
|
-
logger.warn { connection_close.reply_text }
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
# This will be the last thing run by the reactor.
|
74
|
-
shutdown_hook { logger.debug { "Reactor Stopped" } }
|
75
|
-
|
76
|
-
yield if block_given?
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
def shutdown_hook(&block)
|
81
|
-
EM.add_shutdown_hook(&block)
|
82
|
-
end
|
83
|
-
|
84
|
-
def stop(immediately=false, &blk)
|
85
|
-
shutdown_hook(&blk) if blk
|
86
|
-
|
87
|
-
if running?
|
88
|
-
if immediately
|
89
|
-
EM.next_tick do
|
90
|
-
@connection.close { EM.stop_event_loop }
|
91
|
-
end
|
92
|
-
else
|
93
|
-
EM.add_timer(1) do
|
94
|
-
@connection.close { EM.stop_event_loop }
|
95
|
-
end
|
96
|
-
end
|
97
|
-
else
|
98
|
-
logger.fatal { "Eventmachine is not running, exiting with prejudice" }
|
99
|
-
exit!
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
private
|
104
|
-
|
105
|
-
def tcp_connection_failure_handler(settings)
|
106
|
-
# Only display the following settings.
|
107
|
-
s = settings.select { |k,v| ([:user, :pass, :vhost, :host, :port, :ssl].include?(k)) }
|
108
|
-
|
109
|
-
logger.fatal { "Cannot connect to the AMQP server." }
|
110
|
-
logger.fatal { "Is the server running and are the connection details correct?" }
|
111
|
-
logger.info { "Details:" }
|
112
|
-
s.each do |k,v|
|
113
|
-
logger.info { " Setting: %-7s%s" % [k, v] }
|
114
|
-
end
|
115
|
-
EM.stop
|
116
|
-
end
|
117
|
-
|
118
|
-
def authentication_failure_handler(settings)
|
119
|
-
# Only display the following settings.
|
120
|
-
s = settings.select { |k,v| [:user, :pass, :vhost, :host].include?(k) }
|
121
|
-
|
122
|
-
logger.fatal { "Authentication failure." }
|
123
|
-
logger.info { "Details:" }
|
124
|
-
s.each do |k,v|
|
125
|
-
logger.info { " Setting: %-7s%s" % [k, v] }
|
126
|
-
end
|
127
|
-
EM.stop
|
128
|
-
end
|
129
|
-
|
130
|
-
def broker_identifier(connection)
|
131
|
-
broker = connection.broker.properties
|
132
|
-
"#{connection.broker_endpoint}, (#{broker['product']}/v#{broker['version']})"
|
133
|
-
end
|
134
|
-
end
|