a13g 0.1.0.beta3 → 0.1.0.beta4
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +21 -0
- data/Rakefile +53 -0
- data/a13g-0.1.0.beta3.gem +0 -0
- data/a13g.gemspec +100 -0
- data/examples/consumer.rb +16 -0
- data/examples/multiple_connections.rb +26 -0
- data/examples/producer.rb +11 -0
- data/examples/simple_project/README +3 -0
- data/examples/simple_project/Rakefile +6 -0
- data/examples/simple_project/config/broker.yml +10 -0
- data/examples/simple_project/lib/consumers/first_consumer.rb +7 -0
- data/examples/simple_project/lib/consumers/second_consumer.rb +9 -0
- data/examples/simple_project/lib/simple_project.rb +20 -0
- data/lib/a13g.rb +68 -0
- data/lib/a13g/adapters.rb +45 -0
- data/lib/a13g/adapters/abstract_adapter.rb +330 -0
- data/lib/a13g/adapters/stomp_adapter.rb +163 -0
- data/lib/a13g/adapters/test_adapter.rb +102 -0
- data/lib/a13g/base.rb +448 -0
- data/lib/a13g/command.rb +69 -0
- data/lib/a13g/consumer.rb +129 -0
- data/lib/a13g/destination.rb +22 -0
- data/lib/a13g/errors.rb +60 -0
- data/lib/a13g/listener.rb +190 -0
- data/lib/a13g/message.rb +68 -0
- data/lib/a13g/producer.rb +107 -0
- data/lib/a13g/railtie.rb +4 -0
- data/lib/a13g/recipes.rb +31 -0
- data/lib/a13g/subscription.rb +123 -0
- data/lib/a13g/support/logger.rb +194 -0
- data/lib/a13g/support/utils.rb +25 -0
- data/lib/a13g/utils.rb +25 -0
- data/lib/a13g/version.rb +10 -0
- data/spec/a13g_spec.rb +74 -0
- data/spec/config/broker.yml +4 -0
- data/spec/dconfig/broker.yml +4 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +14 -0
- metadata +50 -4
@@ -0,0 +1,163 @@
|
|
1
|
+
module A13g
|
2
|
+
module Adapters
|
3
|
+
class StompAdapter < AbstractAdapter
|
4
|
+
def initialize(config, logger)
|
5
|
+
unless defined? Stomp
|
6
|
+
begin
|
7
|
+
require_library_or_gem('stomp')
|
8
|
+
rescue LoadError
|
9
|
+
$stderr.puts '!!! Please install the stomp gem and try again: gem install stomp.'
|
10
|
+
raise
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
config.symbolize_keys
|
15
|
+
|
16
|
+
config = {
|
17
|
+
:login => '',
|
18
|
+
:passcode => '',
|
19
|
+
:host => 'localhost',
|
20
|
+
:port => config[:ssl] ? 61612 : 61613,
|
21
|
+
:reliable => true,
|
22
|
+
:dead_letter_queue => '/queue/DLQ',
|
23
|
+
:client_id => nil,
|
24
|
+
:max_retry => 0,
|
25
|
+
:max_reconnect_delay => 30.0,
|
26
|
+
:timeout => -1,
|
27
|
+
:ssl => false
|
28
|
+
}.merge(config)
|
29
|
+
|
30
|
+
super(config, logger)
|
31
|
+
|
32
|
+
@connection_options = {
|
33
|
+
:hosts => [
|
34
|
+
{
|
35
|
+
:login => @config[:login],
|
36
|
+
:passcode => @config[:passcode],
|
37
|
+
:host => @config[:host],
|
38
|
+
:port => @config[:port],
|
39
|
+
:ssl => @config[:ssl],
|
40
|
+
:reliable => @config[:reliable],
|
41
|
+
},],
|
42
|
+
:initial_reconnect_delay => 0.01,
|
43
|
+
:max_reconnect_delay => @config[:max_reconnect_delay],
|
44
|
+
:use_exponential_back_off => true,
|
45
|
+
:back_off_multiplier => 2,
|
46
|
+
:max_reconnect_attempts => @config[:max_retry],
|
47
|
+
:randomize => false,
|
48
|
+
:backup => false,
|
49
|
+
:timeout => @config[:timeout]
|
50
|
+
}
|
51
|
+
|
52
|
+
connect
|
53
|
+
end
|
54
|
+
|
55
|
+
def adapter_name
|
56
|
+
'Stomp'.freeze
|
57
|
+
end
|
58
|
+
|
59
|
+
def url
|
60
|
+
"stomp://#{@config[:host]}:#{@config[:port]}" if @config
|
61
|
+
end
|
62
|
+
|
63
|
+
def active?
|
64
|
+
if @connection.respond_to?(:open?)
|
65
|
+
@connection.open?
|
66
|
+
else
|
67
|
+
false
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def reconnect!
|
72
|
+
disconnect!
|
73
|
+
connect
|
74
|
+
end
|
75
|
+
|
76
|
+
def disconnect!(headers={})
|
77
|
+
if active? && @connection.respond_to?(:disconnect)
|
78
|
+
@connection.disconnect(headers)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def ack(message, headers={})
|
83
|
+
if message
|
84
|
+
ack_headers = message.headers.has_key?(:transaction) ? message.headers[:transaction] : {}
|
85
|
+
headers = ack_headers.merge(headers)
|
86
|
+
result = raw_connection.ack(message.headers['message-id'], headers)
|
87
|
+
run_callbacks(:after_ack, [message, headers])
|
88
|
+
result
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def begin(name, headers={})
|
93
|
+
result = raw_connection.begin(name, headers)
|
94
|
+
run_callbacks(:after_begin_transaction, [name, headers])
|
95
|
+
result
|
96
|
+
end
|
97
|
+
|
98
|
+
def commit(name, headers={})
|
99
|
+
result = raw_connection.commit(name, headers)
|
100
|
+
run_callbacks(:after_commit_transaction, [name, headers])
|
101
|
+
result
|
102
|
+
end
|
103
|
+
|
104
|
+
def abort(name, headers={})
|
105
|
+
result = raw_connection.abort(name, headers)
|
106
|
+
run_callbacks(:after_abort_transaction, [name, headers])
|
107
|
+
result
|
108
|
+
end
|
109
|
+
|
110
|
+
def subscribe(destination, headers={}, sub_id=nil)
|
111
|
+
result = raw_connection.subscribe(destination, headers, sub_id)
|
112
|
+
run_callbacks(:after_subscribe, [destination, headers, sub_id])
|
113
|
+
result
|
114
|
+
end
|
115
|
+
|
116
|
+
def unsubscribe(destination, headers={}, sub_id=nil)
|
117
|
+
result = raw_connection.unsubscribe(destination, headers, sub_id=nil)
|
118
|
+
run_callbacks(:after_unsubscribe, [destination, headers, sub_id])
|
119
|
+
result
|
120
|
+
end
|
121
|
+
|
122
|
+
def publish(destination, message, headers={})
|
123
|
+
headers = { :persistent => true }.merge(headers)
|
124
|
+
result = raw_connection.publish(destination, message, headers)
|
125
|
+
run_callbacks(:after_publish, [destination, message, headers])
|
126
|
+
result
|
127
|
+
end
|
128
|
+
|
129
|
+
def receive
|
130
|
+
raw_message = raw_connection.receive
|
131
|
+
message = Message.new(raw_message, raw_message.headers['message-id'],
|
132
|
+
raw_message.headers, raw_message.body, raw_message.command, self)
|
133
|
+
run_callbacks(:after_receive, [message])
|
134
|
+
message
|
135
|
+
end
|
136
|
+
|
137
|
+
def unreceive(message, headers={})
|
138
|
+
defaults = { :dead_letter_queue => @config[:dead_letter_queue] }
|
139
|
+
headers = headers.merge(defaults)
|
140
|
+
result = raw_connection.unreceive(message, headers)
|
141
|
+
run_callbacks(:after_receive, [message, headers])
|
142
|
+
result
|
143
|
+
end
|
144
|
+
|
145
|
+
def client_ack?(message)
|
146
|
+
raw_connection.client_ack?(message)
|
147
|
+
end
|
148
|
+
|
149
|
+
protected
|
150
|
+
|
151
|
+
def connect
|
152
|
+
if @connection = Stomp::Connection.new(@connection_options)
|
153
|
+
logger.debug "Stomp connection with broker at #{url} has been established"
|
154
|
+
else
|
155
|
+
verify!
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
register(:stomp, StompAdapter)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
@@ -0,0 +1,102 @@
|
|
1
|
+
module A13g
|
2
|
+
module Adapters
|
3
|
+
class TestAdapter < AbstractAdapter
|
4
|
+
|
5
|
+
def initialize(config, logger)
|
6
|
+
super(config, logger)
|
7
|
+
@receiving_queue = []
|
8
|
+
connect
|
9
|
+
end
|
10
|
+
|
11
|
+
def adapter_name
|
12
|
+
'Test'.freeze
|
13
|
+
end
|
14
|
+
|
15
|
+
def url
|
16
|
+
''
|
17
|
+
end
|
18
|
+
|
19
|
+
def active?
|
20
|
+
@active ||= false
|
21
|
+
end
|
22
|
+
|
23
|
+
def reconnect!
|
24
|
+
disconnect!
|
25
|
+
connect
|
26
|
+
end
|
27
|
+
|
28
|
+
def disconnect!(headers={})
|
29
|
+
@active = false if active?
|
30
|
+
end
|
31
|
+
|
32
|
+
def ack(message, headers={})
|
33
|
+
if message
|
34
|
+
run_callbacks(:after_ack, [message, headers])
|
35
|
+
true
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def begin(name, headers={})
|
40
|
+
run_callbacks(:after_begin_transaction, [name, headers])
|
41
|
+
true
|
42
|
+
end
|
43
|
+
|
44
|
+
def commit(name, headers={})
|
45
|
+
run_callbacks(:after_commit_transaction, [name, headers])
|
46
|
+
true
|
47
|
+
end
|
48
|
+
|
49
|
+
def abort(name, headers={})
|
50
|
+
run_callbacks(:after_abort_transaction, [name, headers])
|
51
|
+
true
|
52
|
+
end
|
53
|
+
|
54
|
+
def subscribe(destination, headers={}, sub_id=nil)
|
55
|
+
run_callbacks(:after_subscribe, [destination, headers, sub_id])
|
56
|
+
true
|
57
|
+
end
|
58
|
+
|
59
|
+
def unsubscribe(destination, headers={}, sub_id=nil)
|
60
|
+
run_callbacks(:after_unsubscribe, [destination, headers, sub_id])
|
61
|
+
true
|
62
|
+
end
|
63
|
+
|
64
|
+
def publish(destination, message, headers={})
|
65
|
+
headers = {
|
66
|
+
'message-id' => 'TEST',
|
67
|
+
'destination' => destination.to_s
|
68
|
+
}.merge(headers)
|
69
|
+
msg = OpenStruct.new(
|
70
|
+
:body => message,
|
71
|
+
:headers => headers,
|
72
|
+
:command => 'MESSAGE'
|
73
|
+
)
|
74
|
+
@receiving_queue << Message.new(msg, msg.headers['message-id'],
|
75
|
+
msg.headers, message, msg.command, self)
|
76
|
+
run_callbacks(:after_publish, [destination, message, headers])
|
77
|
+
true
|
78
|
+
end
|
79
|
+
|
80
|
+
def receive
|
81
|
+
if message = @receiving_queue.shift
|
82
|
+
run_callbacks(:after_receive, [message])
|
83
|
+
message
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def unreceive(message, headers={})
|
88
|
+
run_callbacks(:after_receive, [message, headers])
|
89
|
+
end
|
90
|
+
|
91
|
+
protected
|
92
|
+
|
93
|
+
def connect
|
94
|
+
@active = true
|
95
|
+
logger.debug "Test connection has been established"
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
register(:test, TestAdapter)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
data/lib/a13g/base.rb
ADDED
@@ -0,0 +1,448 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
module A13g
|
4
|
+
# It describes connection during setup.
|
5
|
+
class ConnectionSpecification
|
6
|
+
attr_reader :config, :adapter
|
7
|
+
def initialize(config, adapter)
|
8
|
+
@config, @adapter = config, adapter
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
# It keeps context informations about connection and related consumers.
|
13
|
+
class ConnectionContext
|
14
|
+
attr_reader :name, :related_classes, :connection
|
15
|
+
|
16
|
+
# Constructor.
|
17
|
+
#
|
18
|
+
# @param [Symbol] name
|
19
|
+
# context name
|
20
|
+
# @param [A13g::Adapters::AbstractAdapter] connection
|
21
|
+
# related connection
|
22
|
+
# @param [Array] related classess
|
23
|
+
# list of consumers woring in this context
|
24
|
+
#
|
25
|
+
# @api public
|
26
|
+
def initialize(name, connection, related_classes=[])
|
27
|
+
@name, @connection, @related_classes = name, connection, related_classes
|
28
|
+
@related_classes << Base if name == :default
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_s # :nodoc:
|
32
|
+
name
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class Base
|
37
|
+
# Returns broker configuration for current connection and environment.
|
38
|
+
#
|
39
|
+
# For example, the following broker.yml...
|
40
|
+
#
|
41
|
+
# development:
|
42
|
+
# adapter: stomp
|
43
|
+
# host: localhost
|
44
|
+
# port: 61613
|
45
|
+
#
|
46
|
+
# ...would result in A13g::Base#config on development environment
|
47
|
+
# to look like this:
|
48
|
+
#
|
49
|
+
# {
|
50
|
+
# 'development' => {
|
51
|
+
# 'adapter' => 'stomp'
|
52
|
+
# 'host' => 'localhost'
|
53
|
+
# 'port' => 61613
|
54
|
+
# }
|
55
|
+
# }
|
56
|
+
#
|
57
|
+
# @api public
|
58
|
+
cattr_reader :configurations
|
59
|
+
@@configurations = HashWithIndifferentAccess.new
|
60
|
+
|
61
|
+
# @api public
|
62
|
+
cattr_accessor :logger, :instance_writer => false
|
63
|
+
@@logger = nil
|
64
|
+
|
65
|
+
# @api public
|
66
|
+
cattr_accessor :environment, :instance_writer => false
|
67
|
+
@@environment = nil
|
68
|
+
|
69
|
+
# @api public
|
70
|
+
cattr_reader :ready
|
71
|
+
@@ready = false
|
72
|
+
|
73
|
+
# Keeps list of connection contexts.
|
74
|
+
#
|
75
|
+
# @api public
|
76
|
+
cattr_reader :contexts
|
77
|
+
@@contexts = HashWithIndifferentAccess.new
|
78
|
+
|
79
|
+
# List of contexts related with consumers.
|
80
|
+
#
|
81
|
+
# @api public
|
82
|
+
cattr_reader :contextuals
|
83
|
+
@@contextuals = {}
|
84
|
+
|
85
|
+
# @api public
|
86
|
+
cattr_reader :path
|
87
|
+
@@path = nil
|
88
|
+
|
89
|
+
# Returns the connection currently associated with the class.
|
90
|
+
#
|
91
|
+
# @return [A13g::Adapters::AbstractAdapter]
|
92
|
+
# currenct connection
|
93
|
+
#
|
94
|
+
# @api public
|
95
|
+
def connection
|
96
|
+
self.class.connection
|
97
|
+
end
|
98
|
+
|
99
|
+
class << self
|
100
|
+
# Returns all pathes to important directories and files.
|
101
|
+
#
|
102
|
+
# You can define following pathes when your project structure is different
|
103
|
+
# than standard Ruby on Rails / Merb / Padrino application.
|
104
|
+
#
|
105
|
+
# A13g::Base.path.app # => path to application
|
106
|
+
# A13g::Base.path.config # => path to broker configuration file
|
107
|
+
# A13g::Base.path.consumers # => path to consumers directory
|
108
|
+
#
|
109
|
+
# There is also shortcut: A13g#path.
|
110
|
+
#
|
111
|
+
# @see A13g#path
|
112
|
+
#
|
113
|
+
# @return [Struct(:root, :log, :config, :consumers)]
|
114
|
+
# important directories pathes
|
115
|
+
#
|
116
|
+
# @api public
|
117
|
+
def path
|
118
|
+
unless @@path
|
119
|
+
@@path = OpenStruct.new
|
120
|
+
@@path.root ||= APP_ROOT if defined?(APP_ROOT)
|
121
|
+
@@path.root ||= RACK_ROOT if defined?(RACK_ROOT)
|
122
|
+
@@path.root ||= RAILS_ROOT if defined?(RAILS_ROOT)
|
123
|
+
@@path.root ||= Rails.root if defined?(Rails)
|
124
|
+
@@path.root ||= "./"
|
125
|
+
@@path.config ||= File.join(@@path.root.to_s, 'config')
|
126
|
+
@@path.consumers ||= File.join(@@path.root.to_s, 'app', 'consumers')
|
127
|
+
end
|
128
|
+
@@path
|
129
|
+
end
|
130
|
+
|
131
|
+
# Current environment.
|
132
|
+
#
|
133
|
+
# @return [String]
|
134
|
+
# env name
|
135
|
+
#
|
136
|
+
# @api public
|
137
|
+
def environment
|
138
|
+
unless @environment
|
139
|
+
@@environment ||= APP_ENV.to_s if defined?(APP_ENV)
|
140
|
+
@@environment ||= RACK_ENV.to_s if defined?(RACK_ENV)
|
141
|
+
@@environment ||= Rails.env.to_s if defined?(Rails)
|
142
|
+
@@environment ||= 'development'
|
143
|
+
end
|
144
|
+
@@environment
|
145
|
+
end
|
146
|
+
|
147
|
+
# Output logger for processor instance. Actually it's global logger for
|
148
|
+
# whole A13g.
|
149
|
+
#
|
150
|
+
# @return [Logger]
|
151
|
+
#
|
152
|
+
# @api public
|
153
|
+
def logger
|
154
|
+
unless @@logger
|
155
|
+
@@logger ||= APP_LOGGER if defined?(APP_LOGGER)
|
156
|
+
@@logger ||= Merb::Logger if defined?(Merb::Logger)
|
157
|
+
@@logger ||= RAILS_DEFAULT_LOGGER if defined?(RAILS_DEFAULT_LOGGER)
|
158
|
+
@@logger ||= Rails.logger if defined?(Rails) && !defined?(RAILS_DEFAULT_LOGGER)
|
159
|
+
@@logger ||= Logger.new(STDOUT)
|
160
|
+
end
|
161
|
+
@@logger
|
162
|
+
end
|
163
|
+
|
164
|
+
# Returns `true` if was successfully prepared to work.
|
165
|
+
#
|
166
|
+
# @return [Boolean]
|
167
|
+
# is A13g ready to work
|
168
|
+
#
|
169
|
+
# @api public
|
170
|
+
def ready?
|
171
|
+
@@ready
|
172
|
+
end
|
173
|
+
|
174
|
+
# Prepares configuration if not ready and creates connection in specified
|
175
|
+
# context.
|
176
|
+
#
|
177
|
+
# Here you have few examples how to setup A13g...
|
178
|
+
#
|
179
|
+
# # ...using configuration hash
|
180
|
+
# A13g::Base.setup(:default, :adapter => 'stomp', :host => 'localhost')
|
181
|
+
#
|
182
|
+
# # ...using configuration for specified environment
|
183
|
+
# A13g::Base.setup(:default, :development)
|
184
|
+
#
|
185
|
+
# # ...using default context and current environment configuration
|
186
|
+
# A13g::Base.setup() # or A13g::Base.setup(:default)
|
187
|
+
#
|
188
|
+
# # ...using shortcut
|
189
|
+
# A13g.setup(:default)
|
190
|
+
#
|
191
|
+
# @param [Context] context
|
192
|
+
# context name
|
193
|
+
# @param [Hash, Symbol, ConnectionSpecification] spec
|
194
|
+
# connection configuration
|
195
|
+
#
|
196
|
+
# @return [A13g::Adapters::AbstractAdapter]
|
197
|
+
# established connection
|
198
|
+
#
|
199
|
+
# @api public
|
200
|
+
def setup(context=:default, spec=nil)
|
201
|
+
unless ready?
|
202
|
+
path.freeze
|
203
|
+
environment.freeze
|
204
|
+
logger
|
205
|
+
if !spec.is_a?(Hash) && !spec.is_a?(ConnectionSpecification)
|
206
|
+
load_config_from_file
|
207
|
+
else
|
208
|
+
@@configurations[context] = HashWithIndifferentAccess.new
|
209
|
+
@@configurations[context][environment] = spec if spec.is_a?(Hash)
|
210
|
+
@@configurations[context][environment] = spec.config if spec.is_a?(ConnectionSpecification)
|
211
|
+
end
|
212
|
+
@@ready = true
|
213
|
+
end
|
214
|
+
establish_connection(context, spec)
|
215
|
+
end
|
216
|
+
|
217
|
+
# Clears all destinations and closes active connections.
|
218
|
+
#
|
219
|
+
# @api public
|
220
|
+
def teardown
|
221
|
+
destroy_subscriptions
|
222
|
+
clear_all_connections!
|
223
|
+
@@logger = nil
|
224
|
+
@@path = nil
|
225
|
+
@@environment = nil
|
226
|
+
@@contextuals = {}
|
227
|
+
@@configurations = HashWithIndifferentAccess.new
|
228
|
+
@@contexts = HashWithIndifferentAccess.new
|
229
|
+
@@ready = false
|
230
|
+
end
|
231
|
+
|
232
|
+
# Connection currently associated with the class or connection from
|
233
|
+
# specified context.
|
234
|
+
#
|
235
|
+
# @param [Symbol] name
|
236
|
+
# context name
|
237
|
+
#
|
238
|
+
# @return [A13g::Adapters::AbstractAdapter]
|
239
|
+
# found connection
|
240
|
+
#
|
241
|
+
# @api public
|
242
|
+
def connection(name=nil)
|
243
|
+
unless name.nil?
|
244
|
+
context = @@contexts[name]
|
245
|
+
if context
|
246
|
+
raise_connection_error(name) unless context.connection
|
247
|
+
return context.connection
|
248
|
+
end
|
249
|
+
else
|
250
|
+
if context
|
251
|
+
raise_connection_error(name) unless context.connection
|
252
|
+
return context.connection
|
253
|
+
else
|
254
|
+
return connection(:default)
|
255
|
+
end
|
256
|
+
end
|
257
|
+
raise_connection_error(:default)
|
258
|
+
end
|
259
|
+
|
260
|
+
# Defines connection context. For consumers can be called only once per class.
|
261
|
+
#
|
262
|
+
# @param [Symbol] name
|
263
|
+
# connection name
|
264
|
+
#
|
265
|
+
# @return [A13g::ConnectionContext]
|
266
|
+
# related context
|
267
|
+
#
|
268
|
+
# @api public
|
269
|
+
def context(name=nil)
|
270
|
+
if name.nil?
|
271
|
+
if @@contextuals[self]
|
272
|
+
return @@contextuals[self]
|
273
|
+
else
|
274
|
+
return @@contexts[:default]
|
275
|
+
end
|
276
|
+
end
|
277
|
+
unless @@contextuals[self]
|
278
|
+
if connection(name)
|
279
|
+
@@contexts[name].related_classes << self
|
280
|
+
@@contextuals[self] = @@contexts[name]
|
281
|
+
end
|
282
|
+
else
|
283
|
+
raise ContextAlreadyDefinedError
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
# Returns true if connected with broker is established and valid.
|
288
|
+
#
|
289
|
+
# @return [Boolean]
|
290
|
+
# connection state
|
291
|
+
#
|
292
|
+
# @api public
|
293
|
+
def connected?
|
294
|
+
connection && connection.active?
|
295
|
+
end
|
296
|
+
|
297
|
+
# Creates all defined subscriptions.
|
298
|
+
#
|
299
|
+
# @api public
|
300
|
+
def create_subscriptions
|
301
|
+
load_consumers
|
302
|
+
Subscription.all.each {|name, subscription| subscription.subscribe }
|
303
|
+
end
|
304
|
+
|
305
|
+
# Destroys all active subscriptions.
|
306
|
+
#
|
307
|
+
# @api public
|
308
|
+
def destroy_subscriptions
|
309
|
+
Subscription.all.each {|name, subscription| subscription.unsubscribe }
|
310
|
+
end
|
311
|
+
|
312
|
+
private
|
313
|
+
|
314
|
+
# Connects to broker using authorization specified in <tt>spec</tt> param.
|
315
|
+
#
|
316
|
+
# === Connection specification options
|
317
|
+
#
|
318
|
+
# * <tt>:adapter</tt> - You can use one of following: stomp, jms, wmq, test,
|
319
|
+
# beanstalk, asqs, realiable_msg
|
320
|
+
# * <tt>:username</tt> - User name for authentication
|
321
|
+
# * <tt>:password</tt> - Password for authentication
|
322
|
+
# * <tt>:host</tt> - Where broker is served
|
323
|
+
# * <tt>:port</tt> - Which port you want to connect
|
324
|
+
#
|
325
|
+
# There are also adapter specific options, like eg. <tt>:max_retry</tt>,
|
326
|
+
# or # <tt>:dead_letter_queue</tt> for stomp adapter.
|
327
|
+
#
|
328
|
+
# @see A13g#setup
|
329
|
+
#
|
330
|
+
# @api private
|
331
|
+
def establish_connection(context, spec=nil)
|
332
|
+
case spec
|
333
|
+
when nil
|
334
|
+
establish_connection(context, environment)
|
335
|
+
when ConnectionSpecification
|
336
|
+
remove_connection(name)
|
337
|
+
adapter = Adapters.load(spec.adapter)
|
338
|
+
conn = adapter.new(spec.config, logger)
|
339
|
+
@@contexts[context] = ConnectionContext.new(context, conn)
|
340
|
+
conn
|
341
|
+
when Symbol, String
|
342
|
+
if configurations[context] && configuration = configurations[context][spec.to_s]
|
343
|
+
establish_connection(context, configuration)
|
344
|
+
else
|
345
|
+
raise AdapterNotSpecified, "Broker in `#{spec}` environment is not configured"
|
346
|
+
end
|
347
|
+
when Hash
|
348
|
+
spec = spec.symbolize_keys
|
349
|
+
establish_connection unless spec.key?(:adapter)
|
350
|
+
establish_connection(context, ConnectionSpecification.new(spec, spec[:adapter]))
|
351
|
+
else
|
352
|
+
raise ArgumentError, "Unknown connection specification"
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
# @param [Symbol] name
|
357
|
+
# context name
|
358
|
+
#
|
359
|
+
# @api private
|
360
|
+
def raise_connection_error(name)
|
361
|
+
if name != :default
|
362
|
+
raise ConnectionNotEstablished, "Connection marked as `#{name}` is not established"
|
363
|
+
else
|
364
|
+
raise MissingDefaultContext, "There is not defined default connection context. Did you forgot about `:default`?"
|
365
|
+
end
|
366
|
+
nil
|
367
|
+
end
|
368
|
+
|
369
|
+
# Removes specified connection.
|
370
|
+
#
|
371
|
+
# @param [Symbol] name
|
372
|
+
# context name
|
373
|
+
#
|
374
|
+
# @api private
|
375
|
+
def remove_connection(context)
|
376
|
+
context = @@contexts.delete(context)
|
377
|
+
context.connection.disconnect! if context && context.connection
|
378
|
+
end
|
379
|
+
|
380
|
+
# Close all active connections.
|
381
|
+
#
|
382
|
+
# @api private
|
383
|
+
def clear_active_connections!
|
384
|
+
@@contexts.values.each do |context|
|
385
|
+
if c = context.connection
|
386
|
+
c.disconnect! if c.active?
|
387
|
+
end
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
# Close all connections.
|
392
|
+
#
|
393
|
+
# @api private
|
394
|
+
def clear_all_connections!
|
395
|
+
@@contexts.values.each do |context|
|
396
|
+
if c = context.connection
|
397
|
+
c.disconnect!
|
398
|
+
end
|
399
|
+
end
|
400
|
+
end
|
401
|
+
|
402
|
+
# Verify all connections.
|
403
|
+
#
|
404
|
+
# @api private
|
405
|
+
def verify_active_connections!
|
406
|
+
@@contexts.values.each do |context|
|
407
|
+
if c = context.connection
|
408
|
+
c.verify!
|
409
|
+
end
|
410
|
+
end
|
411
|
+
end
|
412
|
+
|
413
|
+
# Load configurations from file.
|
414
|
+
#
|
415
|
+
# @param [Symbol] context
|
416
|
+
# context name
|
417
|
+
#
|
418
|
+
# @return [HashWithIndifferentAccess]
|
419
|
+
# configuration hash
|
420
|
+
#
|
421
|
+
# @api private
|
422
|
+
def load_config_from_file(context=:default)
|
423
|
+
fname = context == :default ? "broker.yml" : "broker-#{context.to_s}.yml"
|
424
|
+
cpath = File.join(@@path.config, fname)
|
425
|
+
if File.exists?(cpath)
|
426
|
+
conf = YAML.load_file(cpath)
|
427
|
+
@@configurations[context] = HashWithIndifferentAccess.new(conf)
|
428
|
+
end
|
429
|
+
end
|
430
|
+
|
431
|
+
# Loads all available consumers.
|
432
|
+
#
|
433
|
+
# @api private
|
434
|
+
def load_consumers
|
435
|
+
if File.exists?(path.consumers)
|
436
|
+
begin
|
437
|
+
load File.join(path.consumers, 'application_consumer.rb')
|
438
|
+
rescue LoadError
|
439
|
+
# Do nothing... It is allowed to skip ApplicationConsumer.
|
440
|
+
end
|
441
|
+
Dir[File.join(path.consumers, '*_consumer.rb')].each do |f|
|
442
|
+
load f unless f =~ /\/application_consumer.rb\Z/
|
443
|
+
end
|
444
|
+
end
|
445
|
+
end
|
446
|
+
end
|
447
|
+
end
|
448
|
+
end
|