euston-rabbitmq 1.0.0-java
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/Gemfile +2 -0
- data/Gemfile.lock +103 -0
- data/Rakefile +182 -0
- data/euston-rabbitmq.gemspec +74 -0
- data/lib/euston-rabbitmq/buffered_message_dispatcher.rb +48 -0
- data/lib/euston-rabbitmq/command_handler_bindings.rb +30 -0
- data/lib/euston-rabbitmq/command_handlers/retry_failed_message.rb +29 -0
- data/lib/euston-rabbitmq/errors.rb +5 -0
- data/lib/euston-rabbitmq/event_handler_binding.rb +114 -0
- data/lib/euston-rabbitmq/event_handler_bindings.rb +30 -0
- data/lib/euston-rabbitmq/event_handlers/message_failure.rb +27 -0
- data/lib/euston-rabbitmq/event_store_dispatcher.rb +45 -0
- data/lib/euston-rabbitmq/exchanges.rb +17 -0
- data/lib/euston-rabbitmq/handler_bindings.rb +115 -0
- data/lib/euston-rabbitmq/message_buffer.rb +68 -0
- data/lib/euston-rabbitmq/message_logger.rb +50 -0
- data/lib/euston-rabbitmq/queue.rb +30 -0
- data/lib/euston-rabbitmq/queues.rb +13 -0
- data/lib/euston-rabbitmq/read_model/failed_message.rb +36 -0
- data/lib/euston-rabbitmq/read_model/message_buffer.rb +52 -0
- data/lib/euston-rabbitmq/read_model/message_log.rb +37 -0
- data/lib/euston-rabbitmq/version.rb +5 -0
- data/lib/euston-rabbitmq.rb +28 -0
- data/spec/command_buffer_spec.rb +69 -0
- data/spec/event_buffer_spec.rb +69 -0
- data/spec/exchange_declaration_spec.rb +28 -0
- data/spec/message_failure_spec.rb +77 -0
- data/spec/mt_safe_queue_subscription_spec.rb +72 -0
- data/spec/safe_queue_subscription_spec.rb +50 -0
- data/spec/spec_helper.rb +65 -0
- data/spec/support/factories.rb +18 -0
- metadata +233 -0
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
euston-rabbitmq (1.0.0)
|
5
|
+
activesupport (~> 3.0.0)
|
6
|
+
amqp (~> 0.8.0)
|
7
|
+
bson_ext (~> 1.3.1)
|
8
|
+
euston (~> 1.0.0)
|
9
|
+
euston-eventstore (~> 1.0.0)
|
10
|
+
eventmachine (~> 0.12.10)
|
11
|
+
hash-keys (~> 1.0.0)
|
12
|
+
hollywood (~> 1.0.0)
|
13
|
+
mongo (~> 1.3.1)
|
14
|
+
require_all (~> 1.2.0)
|
15
|
+
robustthread (~> 0.5.2)
|
16
|
+
safely (~> 0.3.0)
|
17
|
+
|
18
|
+
GEM
|
19
|
+
remote: http://rubygems.org/
|
20
|
+
specs:
|
21
|
+
activesupport (3.0.10)
|
22
|
+
amq-client (0.8.3)
|
23
|
+
amq-protocol (>= 0.8.0)
|
24
|
+
eventmachine
|
25
|
+
amq-protocol (0.8.1)
|
26
|
+
amqp (0.8.0)
|
27
|
+
amq-client (~> 0.8.3)
|
28
|
+
amq-protocol (~> 0.8.0)
|
29
|
+
eventmachine
|
30
|
+
awesome_print (0.4.0)
|
31
|
+
bson (1.3.1)
|
32
|
+
bson (1.3.1-java)
|
33
|
+
bson_ext (1.3.1)
|
34
|
+
diff-lcs (1.1.3)
|
35
|
+
euston (1.0.0)
|
36
|
+
activesupport (~> 3.0.9)
|
37
|
+
euston-eventstore (~> 1.0.0)
|
38
|
+
require_all (~> 1.2.0)
|
39
|
+
euston-eventstore (1.0.4)
|
40
|
+
activesupport (~> 3.0.9)
|
41
|
+
bson_ext (~> 1.3.1)
|
42
|
+
hash-keys (~> 1.0.0)
|
43
|
+
json (~> 1.5.0)
|
44
|
+
mongo (~> 1.3.1)
|
45
|
+
require_all (~> 1.2.0)
|
46
|
+
uuid (~> 2.3.0)
|
47
|
+
euston-eventstore (1.0.4-java)
|
48
|
+
activesupport (~> 3.0.9)
|
49
|
+
hash-keys (~> 1.0.0)
|
50
|
+
jmongo (~> 1.0.0)
|
51
|
+
json-jruby (~> 1.5.0)
|
52
|
+
require_all (~> 1.2.0)
|
53
|
+
uuid (~> 2.3.0)
|
54
|
+
evented-spec (0.9.0)
|
55
|
+
eventmachine (0.12.10)
|
56
|
+
eventmachine (0.12.10-java)
|
57
|
+
faker (1.0.0)
|
58
|
+
i18n (~> 0.4)
|
59
|
+
fuubar (0.0.6)
|
60
|
+
rspec (~> 2.0)
|
61
|
+
rspec-instafail (~> 0.1.8)
|
62
|
+
ruby-progressbar (~> 0.0.10)
|
63
|
+
hash-keys (1.0.0)
|
64
|
+
hollywood (1.0.0)
|
65
|
+
i18n (0.6.0)
|
66
|
+
jmongo (1.0.3)
|
67
|
+
require_all (~> 1.2)
|
68
|
+
json (1.5.0)
|
69
|
+
json (1.5.0-java)
|
70
|
+
json-jruby (1.5.0-java)
|
71
|
+
json (= 1.5.0)
|
72
|
+
macaddr (1.4.0)
|
73
|
+
systemu (~> 2.2.0)
|
74
|
+
mongo (1.3.1)
|
75
|
+
bson (>= 1.3.1)
|
76
|
+
require_all (1.2.0)
|
77
|
+
robustthread (0.5.2)
|
78
|
+
rspec (2.6.0)
|
79
|
+
rspec-core (~> 2.6.0)
|
80
|
+
rspec-expectations (~> 2.6.0)
|
81
|
+
rspec-mocks (~> 2.6.0)
|
82
|
+
rspec-core (2.6.4)
|
83
|
+
rspec-expectations (2.6.0)
|
84
|
+
diff-lcs (~> 1.1.2)
|
85
|
+
rspec-instafail (0.1.8)
|
86
|
+
rspec-mocks (2.6.0)
|
87
|
+
ruby-progressbar (0.0.10)
|
88
|
+
safely (0.3.0)
|
89
|
+
systemu (2.2.0)
|
90
|
+
uuid (2.3.4)
|
91
|
+
macaddr (~> 1.0)
|
92
|
+
|
93
|
+
PLATFORMS
|
94
|
+
java
|
95
|
+
ruby
|
96
|
+
|
97
|
+
DEPENDENCIES
|
98
|
+
awesome_print (~> 0.4.0)
|
99
|
+
euston-rabbitmq!
|
100
|
+
evented-spec (~> 0.9.0)
|
101
|
+
faker (~> 1.0.0)
|
102
|
+
fuubar (~> 0.0.0)
|
103
|
+
rspec (~> 2.6.0)
|
data/Rakefile
ADDED
@@ -0,0 +1,182 @@
|
|
1
|
+
require 'date'
|
2
|
+
require 'rspec/core/rake_task'
|
3
|
+
|
4
|
+
#############################################################################
|
5
|
+
#
|
6
|
+
# Helper functions
|
7
|
+
#
|
8
|
+
#############################################################################
|
9
|
+
|
10
|
+
def name
|
11
|
+
@name ||= Dir['*.gemspec'].first.split('.').first
|
12
|
+
end
|
13
|
+
|
14
|
+
def version
|
15
|
+
line = File.read("lib/#{name}/version.rb")[/^\s*VERSION\s*=\s*.*/]
|
16
|
+
line.match(/.*VERSION\s*=\s*['"](.*)['"]/)[1]
|
17
|
+
end
|
18
|
+
|
19
|
+
def date
|
20
|
+
Date.today.to_s
|
21
|
+
end
|
22
|
+
|
23
|
+
def rubyforge_project
|
24
|
+
name
|
25
|
+
end
|
26
|
+
|
27
|
+
def gemspec_file
|
28
|
+
"#{name}.gemspec"
|
29
|
+
end
|
30
|
+
|
31
|
+
def gem_file
|
32
|
+
"#{name}-#{version}.gem"
|
33
|
+
end
|
34
|
+
|
35
|
+
def replace_header(head, header_name, provider = nil)
|
36
|
+
if provider
|
37
|
+
value = send(provider)
|
38
|
+
else
|
39
|
+
value = "'#{send(header_name)}'"
|
40
|
+
end
|
41
|
+
|
42
|
+
provider ||= header_name
|
43
|
+
head.sub!(/(\.#{header_name}\s*= ).*/) { "#{$1}#{value}"}
|
44
|
+
end
|
45
|
+
|
46
|
+
def platform
|
47
|
+
jruby? ? '-java' : ''
|
48
|
+
end
|
49
|
+
|
50
|
+
def platform_dependant_gem_file
|
51
|
+
"#{name}-#{version}#{platform}.gem"
|
52
|
+
end
|
53
|
+
|
54
|
+
def platform_dependent_version
|
55
|
+
"'#{version}#{platform}'"
|
56
|
+
end
|
57
|
+
|
58
|
+
def jruby?
|
59
|
+
RUBY_PLATFORM.to_s == 'java'
|
60
|
+
end
|
61
|
+
|
62
|
+
def trim_array_ends array
|
63
|
+
array.shift
|
64
|
+
array.pop
|
65
|
+
array
|
66
|
+
end
|
67
|
+
|
68
|
+
#############################################################################
|
69
|
+
#
|
70
|
+
# Custom tasks
|
71
|
+
#
|
72
|
+
#############################################################################
|
73
|
+
|
74
|
+
default_rspec_opts = %w[--colour --format Fuubar]
|
75
|
+
|
76
|
+
desc "Run all examples"
|
77
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
78
|
+
t.rspec_opts = default_rspec_opts
|
79
|
+
end
|
80
|
+
|
81
|
+
desc "Resets the testing vhost in rabbitmq"
|
82
|
+
task :reset_rabbitmq do
|
83
|
+
vhosts = `rabbitmqctl list_vhosts`.split("\n")
|
84
|
+
vhosts = trim_array_ends vhosts
|
85
|
+
vhost = '/euston-rabbitmq-test'
|
86
|
+
|
87
|
+
puts `rabbitmqctl delete_vhost #{vhost}` if vhosts.include? vhost
|
88
|
+
puts `rabbitmqctl add_vhost #{vhost}`
|
89
|
+
|
90
|
+
users = `rabbitmqctl list_users`.split("\n")
|
91
|
+
users = trim_array_ends users
|
92
|
+
users = users.map { |u| u.split("\t").shift }
|
93
|
+
|
94
|
+
user, password = 'euston-rabbitmq-tester', 'password'
|
95
|
+
|
96
|
+
puts `rabbitmqctl delete_user #{user}` if users.include? user
|
97
|
+
puts `rabbitmqctl add_user #{user} #{password}`
|
98
|
+
puts `rabbitmqctl set_permissions -p #{vhost} #{user} ".*" ".*" ".*"`
|
99
|
+
puts `rabbitmqctl set_permissions -p #{vhost} guest ".*" ".*" ".*"` if users.include? 'guest'
|
100
|
+
end
|
101
|
+
|
102
|
+
#############################################################################
|
103
|
+
#
|
104
|
+
# Packaging tasks
|
105
|
+
#
|
106
|
+
#############################################################################
|
107
|
+
|
108
|
+
def built_gem
|
109
|
+
@built_gem ||= Dir["#{name}*.gem"].first
|
110
|
+
end
|
111
|
+
|
112
|
+
desc "Create tag v#{platform_dependent_version} and build and push #{platform_dependant_gem_file} to Rubygems"
|
113
|
+
task :release => :build do
|
114
|
+
unless `git branch` =~ /^\* master$/
|
115
|
+
puts "You must be on the master branch to release!"
|
116
|
+
exit!
|
117
|
+
end
|
118
|
+
|
119
|
+
sh "git commit --allow-empty -a -m 'Release #{platform_dependent_version}'"
|
120
|
+
sh "git tag v#{platform_dependent_version}"
|
121
|
+
sh "git push origin master"
|
122
|
+
sh "git push origin v#{platform_dependent_version}"
|
123
|
+
|
124
|
+
command = "gem push pkg/#{platform_dependant_gem_file}"
|
125
|
+
|
126
|
+
if jruby?
|
127
|
+
puts "--------------------------------------------------------------------------------------"
|
128
|
+
puts "can't push to rubygems using jruby at the moment, so switch to mri and run: #{command}"
|
129
|
+
puts "--------------------------------------------------------------------------------------"
|
130
|
+
else
|
131
|
+
sh command
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
desc "Build #{platform_dependant_gem_file} into the pkg directory"
|
136
|
+
task :build => :gemspec do
|
137
|
+
sh "mkdir -p pkg"
|
138
|
+
sh "gem build #{gemspec_file}"
|
139
|
+
sh "mv #{built_gem} pkg"
|
140
|
+
end
|
141
|
+
|
142
|
+
desc "Generate #{gemspec_file}"
|
143
|
+
task :gemspec => :validate do
|
144
|
+
# read spec file and split out manifest section
|
145
|
+
spec = File.read(gemspec_file)
|
146
|
+
head, manifest, tail = spec.split(" # = MANIFEST =\n")
|
147
|
+
|
148
|
+
# replace name version and date
|
149
|
+
replace_header(head, :name)
|
150
|
+
replace_header(head, :version)
|
151
|
+
replace_header(head, :date)
|
152
|
+
#comment this out if your rubyforge_project has a different name
|
153
|
+
#replace_header(head, :rubyforge_project)
|
154
|
+
|
155
|
+
# determine file list from git ls-files
|
156
|
+
files = `git ls-files`.
|
157
|
+
split("\n").
|
158
|
+
sort.
|
159
|
+
reject { |file| file =~ /^\./ }.
|
160
|
+
reject { |file| file =~ /^(rdoc|pkg)/ }.
|
161
|
+
map { |file| " #{file}" }.
|
162
|
+
join("\n")
|
163
|
+
|
164
|
+
# piece file back together and write
|
165
|
+
manifest = " s.files = %w[\n#{files}\n ]\n"
|
166
|
+
spec = [head, manifest, tail].join(" # = MANIFEST =\n")
|
167
|
+
File.open(gemspec_file, 'w') { |io| io.write(spec) }
|
168
|
+
puts "Updated #{gemspec_file}"
|
169
|
+
end
|
170
|
+
|
171
|
+
desc "Validate #{gemspec_file}"
|
172
|
+
task :validate do
|
173
|
+
libfiles = Dir['lib/*'] - ["lib/#{name}.rb", "lib/#{name}"]
|
174
|
+
unless libfiles.empty?
|
175
|
+
puts "Directory `lib` should only contain a `#{name}.rb` file and `#{name}` dir."
|
176
|
+
exit!
|
177
|
+
end
|
178
|
+
unless Dir['VERSION*'].empty?
|
179
|
+
puts "A `VERSION` file at root level violates Gem best practices."
|
180
|
+
exit!
|
181
|
+
end
|
182
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'euston-rabbitmq'
|
3
|
+
s.version = '1.0.0'
|
4
|
+
s.platform = RUBY_PLATFORM.to_s == 'java' ? 'java' : Gem::Platform::RUBY
|
5
|
+
s.authors = ['Lee Henson', 'Guy Boertje']
|
6
|
+
s.email = ['lee.m.henson@gmail.com', 'guyboertje@gmail.com']
|
7
|
+
s.summary = %q{RabbitMq bindings for Euston.}
|
8
|
+
s.description = "JRuby RabbitMq bindings"
|
9
|
+
s.homepage = 'http://github.com/leemhenson/euston-rabbitmq'
|
10
|
+
|
11
|
+
# = MANIFEST =
|
12
|
+
s.files = %w[
|
13
|
+
Gemfile
|
14
|
+
Gemfile.lock
|
15
|
+
Rakefile
|
16
|
+
euston-rabbitmq.gemspec
|
17
|
+
lib/euston-rabbitmq.rb
|
18
|
+
lib/euston-rabbitmq/buffered_message_dispatcher.rb
|
19
|
+
lib/euston-rabbitmq/command_handler_bindings.rb
|
20
|
+
lib/euston-rabbitmq/command_handlers/retry_failed_message.rb
|
21
|
+
lib/euston-rabbitmq/errors.rb
|
22
|
+
lib/euston-rabbitmq/event_handler_binding.rb
|
23
|
+
lib/euston-rabbitmq/event_handler_bindings.rb
|
24
|
+
lib/euston-rabbitmq/event_handlers/message_failure.rb
|
25
|
+
lib/euston-rabbitmq/event_store_dispatcher.rb
|
26
|
+
lib/euston-rabbitmq/exchanges.rb
|
27
|
+
lib/euston-rabbitmq/handler_bindings.rb
|
28
|
+
lib/euston-rabbitmq/message_buffer.rb
|
29
|
+
lib/euston-rabbitmq/message_logger.rb
|
30
|
+
lib/euston-rabbitmq/queue.rb
|
31
|
+
lib/euston-rabbitmq/queues.rb
|
32
|
+
lib/euston-rabbitmq/read_model/failed_message.rb
|
33
|
+
lib/euston-rabbitmq/read_model/message_buffer.rb
|
34
|
+
lib/euston-rabbitmq/read_model/message_log.rb
|
35
|
+
lib/euston-rabbitmq/version.rb
|
36
|
+
spec/command_buffer_spec.rb
|
37
|
+
spec/event_buffer_spec.rb
|
38
|
+
spec/exchange_declaration_spec.rb
|
39
|
+
spec/message_failure_spec.rb
|
40
|
+
spec/mt_safe_queue_subscription_spec.rb
|
41
|
+
spec/safe_queue_subscription_spec.rb
|
42
|
+
spec/spec_helper.rb
|
43
|
+
spec/support/factories.rb
|
44
|
+
]
|
45
|
+
# = MANIFEST =
|
46
|
+
|
47
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
48
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
49
|
+
|
50
|
+
s.add_dependency 'activesupport', '~> 3.0.0'
|
51
|
+
s.add_dependency 'euston', '~> 1.0.0'
|
52
|
+
s.add_dependency 'euston-eventstore', '~> 1.0.0'
|
53
|
+
s.add_dependency 'hash-keys', '~> 1.0.0'
|
54
|
+
s.add_dependency 'hollywood', '~> 1.0.0'
|
55
|
+
s.add_dependency 'require_all', '~> 1.2.0'
|
56
|
+
s.add_dependency 'robustthread', '~> 0.5.2'
|
57
|
+
s.add_dependency 'safely', '~> 0.3.0'
|
58
|
+
|
59
|
+
if RUBY_PLATFORM.to_s == 'java'
|
60
|
+
s.add_dependency 'jmongo', '~> 1.0.0'
|
61
|
+
s.add_dependency 'jessica', '~> 1.0.0'
|
62
|
+
else
|
63
|
+
s.add_dependency 'amqp', '~> 0.8.0'
|
64
|
+
s.add_dependency 'bson_ext', '~> 1.3.1'
|
65
|
+
s.add_dependency 'eventmachine', '~> 0.12.10'
|
66
|
+
s.add_dependency 'mongo', '~> 1.3.1'
|
67
|
+
s.add_development_dependency 'evented-spec', '~> 0.9.0'
|
68
|
+
end
|
69
|
+
|
70
|
+
s.add_development_dependency 'awesome_print', '~> 0.4.0'
|
71
|
+
s.add_development_dependency 'faker', '~> 1.0.0'
|
72
|
+
s.add_development_dependency 'fuubar', '~> 0.0.0'
|
73
|
+
s.add_development_dependency 'rspec', '~> 2.6.0'
|
74
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Euston
|
2
|
+
module RabbitMq
|
3
|
+
class BufferedMessageDispatcher
|
4
|
+
class << self
|
5
|
+
def command_dispatcher channel
|
6
|
+
@command_dispatcher ||= BufferedMessageDispatcher.new(Euston::RabbitMq::MessageBuffer.commands_buffer channel)
|
7
|
+
end
|
8
|
+
|
9
|
+
def event_dispatcher channel
|
10
|
+
@event_dispatcher ||= BufferedMessageDispatcher.new(Euston::RabbitMq::MessageBuffer.events_buffer channel)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
include Euston::RabbitMq::Exchanges
|
15
|
+
|
16
|
+
def initialize buffer
|
17
|
+
@buffer = buffer
|
18
|
+
@buffer.when(:no_messages_were_due) { @sleep = true }
|
19
|
+
end
|
20
|
+
|
21
|
+
attr_writer :wait_time
|
22
|
+
|
23
|
+
def wait_time
|
24
|
+
@wait_time ||= 1
|
25
|
+
end
|
26
|
+
|
27
|
+
def stop
|
28
|
+
@stop = true
|
29
|
+
end
|
30
|
+
|
31
|
+
def start
|
32
|
+
@stop = false
|
33
|
+
|
34
|
+
dispatch_loop = Proc.new do
|
35
|
+
@sleep = false
|
36
|
+
|
37
|
+
begin
|
38
|
+
@buffer.dispatch_due_messages
|
39
|
+
end while !@sleep
|
40
|
+
|
41
|
+
EM.add_timer(wait_time) { dispatch_loop.call } unless @stop
|
42
|
+
end
|
43
|
+
|
44
|
+
EM.add_timer(wait_time) { dispatch_loop.call }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Euston
|
2
|
+
module RabbitMq
|
3
|
+
class CommandHandlerBindings < HandlerBindings
|
4
|
+
def finalize_bindings
|
5
|
+
super
|
6
|
+
attach_queue_hook_listeners queue
|
7
|
+
end
|
8
|
+
|
9
|
+
protected
|
10
|
+
|
11
|
+
def bind_handler handler_type
|
12
|
+
queue.bind @exchanges[:commands], :routing_key => "commands.#{handler_type.to_s.underscore}"
|
13
|
+
end
|
14
|
+
|
15
|
+
def call_handler message
|
16
|
+
command_headers = CommandHeaders.from_hash(message[:headers]).freeze
|
17
|
+
command_body = message[:body].freeze
|
18
|
+
handler_type = command_headers.type.to_s.camelize.to_sym
|
19
|
+
find_namespaces_containing_handler_type(handler_type).each do |namespace|
|
20
|
+
handler = namespace.const_get handler_type
|
21
|
+
handler.new.send "__version__#{command_headers.version}", command_headers, command_body
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def queue
|
26
|
+
@queue ||= get_queue @channel, :command_handlers
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Euston
|
2
|
+
module RabbitMq
|
3
|
+
module CommandHandlers
|
4
|
+
class RetryFailedMessage
|
5
|
+
include Euston::CommandHandler
|
6
|
+
|
7
|
+
version 1 do |headers, command|
|
8
|
+
model = Euston::RabbitMq::ReadModel::FailedMessage.new
|
9
|
+
message = model.get_by_id command[:id]
|
10
|
+
|
11
|
+
unless message.nil?
|
12
|
+
routing_key = message['routing_key']
|
13
|
+
exchange = routing_key.split('.').first
|
14
|
+
|
15
|
+
headers = message['headers'].merge(:id => command[:id],
|
16
|
+
:type => message['type'],
|
17
|
+
:version => message['version'] )
|
18
|
+
|
19
|
+
buffer = Euston::RabbitMq::MessageBuffer.send("#{exchange}_buffer")
|
20
|
+
buffer.push :headers => headers,
|
21
|
+
:body => message['body']
|
22
|
+
|
23
|
+
model.remove_by_id command[:id]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
module Euston
|
2
|
+
module RabbitMq
|
3
|
+
class EventHandlerBinding
|
4
|
+
include Euston::RabbitMq::Queues
|
5
|
+
include Euston::RabbitMq::Exchanges
|
6
|
+
|
7
|
+
PREFIX = '__event_handler__'
|
8
|
+
|
9
|
+
def initialize(channel, namespace, handler_type)
|
10
|
+
@channel, @namespace, @handler_type = channel, namespace, handler_type
|
11
|
+
|
12
|
+
name = @handler_type.to_s.underscore
|
13
|
+
@queue = get_queue(@channel, name)
|
14
|
+
instance_variable_set "@#{name}".to_sym, @queue
|
15
|
+
@handler_class = @namespace.const_get @handler_type
|
16
|
+
@exchange = get_exchange(@channel, :events)
|
17
|
+
end
|
18
|
+
|
19
|
+
def bind
|
20
|
+
bindable_methods.each do |method|
|
21
|
+
@queue.bind @exchange, :routing_key => "events.#{method}"
|
22
|
+
end
|
23
|
+
self
|
24
|
+
end
|
25
|
+
|
26
|
+
def listen
|
27
|
+
attach_queue_hook_listeners @queue
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def attach_queue_hook_listeners queue
|
33
|
+
queue.when(:message_decode_failed => method(:log_decode_failure),
|
34
|
+
:message_failed => method(:handle_failure),
|
35
|
+
:message_received => method(:call_handler))
|
36
|
+
|
37
|
+
queue.safe_subscribe
|
38
|
+
end
|
39
|
+
|
40
|
+
def call_handler(message)
|
41
|
+
event_headers = EventHeaders.from_hash message[:headers]
|
42
|
+
event_body = message[:body]
|
43
|
+
|
44
|
+
@handler_class.new.send "#{PREFIX}#{event_headers.type}__#{event_headers.version}", event_headers.freeze, event_body.freeze
|
45
|
+
end
|
46
|
+
|
47
|
+
def bindable_methods
|
48
|
+
methods = @handler_class.public_instance_methods(false)
|
49
|
+
methods = methods.select { |method| method.to_s.start_with? PREFIX }
|
50
|
+
methods = methods.map { |method| method.to_s[PREFIX.length, method.to_s.length - PREFIX.length] }
|
51
|
+
methods = methods.map { |method| method.split('__').first }
|
52
|
+
methods.uniq
|
53
|
+
end
|
54
|
+
|
55
|
+
def create_message_failed_message error, failed_message, amqp_header
|
56
|
+
{
|
57
|
+
:headers =>
|
58
|
+
{
|
59
|
+
:id => Euston.uuid.generate.to_s,
|
60
|
+
:type => :message_failed,
|
61
|
+
:version => 1,
|
62
|
+
:timestamp => Time.now.to_f
|
63
|
+
},
|
64
|
+
|
65
|
+
:body =>
|
66
|
+
{
|
67
|
+
:message => failed_message,
|
68
|
+
:routing_key => amqp_header.method.routing_key,
|
69
|
+
:error => error.message,
|
70
|
+
:backtrace => error.backtrace
|
71
|
+
}
|
72
|
+
}
|
73
|
+
end
|
74
|
+
|
75
|
+
def handle_failure message, error, amqp_header
|
76
|
+
failures = message[:failures] || 0
|
77
|
+
failures = failures + 1
|
78
|
+
message[:failures] = failures
|
79
|
+
|
80
|
+
begin
|
81
|
+
if failures == 3
|
82
|
+
message.delete :failures
|
83
|
+
message = create_message_failed_message error, message, amqp_header
|
84
|
+
|
85
|
+
options = { :key => 'events.message_failed' }
|
86
|
+
exchange_ref = :events
|
87
|
+
else
|
88
|
+
options = { :key => amqp_header.method.routing_key }
|
89
|
+
exchange_ref = amqp_header.method.routing_key.split('.').first.to_sym
|
90
|
+
end
|
91
|
+
|
92
|
+
options = default_publish_options.merge options
|
93
|
+
exchange = get_exchange(@channel, exchange_ref)
|
94
|
+
message = ::ActiveSupport::JSON.encode message
|
95
|
+
|
96
|
+
exchange.publish message, options
|
97
|
+
|
98
|
+
amqp_header.ack
|
99
|
+
rescue StandardError => e
|
100
|
+
amqp_header.reject :requeue => true
|
101
|
+
raise e
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def log_decode_failure message, error
|
106
|
+
text = "A handler queue subscription failed. [Error] #{error.message} [Payload] #{message}"
|
107
|
+
err = Euston::RabbitMq::MessageDecodeFailedError.new text
|
108
|
+
err.set_backtrace error.backtrace
|
109
|
+
|
110
|
+
Safely.report! err
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Euston
|
2
|
+
module RabbitMq
|
3
|
+
class EventHandlerBindings < HandlerBindings
|
4
|
+
PREFIX = '__event_handler__'
|
5
|
+
|
6
|
+
protected
|
7
|
+
|
8
|
+
def bind_handler handler_type
|
9
|
+
name = handler_type.to_s.underscore
|
10
|
+
queue = get_queue @channel, name
|
11
|
+
instance_variable_set "@#{name}".to_sym, queue
|
12
|
+
|
13
|
+
namespace = find_namespaces_containing_handler_type(handler_type).first
|
14
|
+
handler_class = namespace.const_get handler_type
|
15
|
+
methods = handler_class.public_instance_methods(false)
|
16
|
+
methods = methods.select { |method| method.to_s.start_with? PREFIX }
|
17
|
+
methods = methods.map { |method| method.to_s[PREFIX.length, method.to_s.length - PREFIX.length] }
|
18
|
+
methods = methods.map { |method| method.split('__').first }
|
19
|
+
methods.uniq.each { |method| queue.bind @exchanges[:events], :routing_key => "events.#{method}" }
|
20
|
+
|
21
|
+
attach_queue_hook_listeners queue do |message|
|
22
|
+
event_headers = EventHeaders.from_hash message[:headers]
|
23
|
+
event_body = message[:body]
|
24
|
+
|
25
|
+
handler_class.new.send "#{PREFIX}#{event_headers.type}__#{event_headers.version}", event_headers.freeze, event_body.freeze
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Euston
|
2
|
+
module RabbitMq
|
3
|
+
module EventHandlers
|
4
|
+
class MessageFailure
|
5
|
+
include Euston::EventHandler
|
6
|
+
|
7
|
+
consumes :message_failed, 1 do |headers, event|
|
8
|
+
headers = event[:message][:headers].dup
|
9
|
+
failure = { :message_id => headers.delete(:id),
|
10
|
+
:type => headers.delete(:type),
|
11
|
+
:version => headers.delete(:version),
|
12
|
+
:message_timestamp => headers.delete(:timestamp),
|
13
|
+
:routing_key => event[:routing_key],
|
14
|
+
:body => event[:message][:body],
|
15
|
+
:headers => headers,
|
16
|
+
:error => event[:error],
|
17
|
+
:backtrace => event[:backtrace],
|
18
|
+
:failure_timestamp => Time.now.to_f }
|
19
|
+
|
20
|
+
model = Euston::RabbitMq::ReadModel::FailedMessage
|
21
|
+
model = model.new
|
22
|
+
model.log_failure failure
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Euston
|
2
|
+
module RabbitMq
|
3
|
+
class EventStoreDispatcher
|
4
|
+
def initialize channel
|
5
|
+
@channel = channel
|
6
|
+
@event_buffer = Euston::RabbitMq::MessageBuffer.events_buffer channel
|
7
|
+
@event_store = Euston::RabbitMq.event_store
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_writer :wait_time
|
11
|
+
|
12
|
+
def wait_time
|
13
|
+
@wait_time ||= 1
|
14
|
+
end
|
15
|
+
|
16
|
+
def stop
|
17
|
+
@stop = true
|
18
|
+
end
|
19
|
+
|
20
|
+
def start
|
21
|
+
@stop = false
|
22
|
+
|
23
|
+
dispatch_loop = Proc.new do
|
24
|
+
begin
|
25
|
+
begin
|
26
|
+
commits = @event_store.get_undispatched_commits
|
27
|
+
|
28
|
+
commits.each do |commit|
|
29
|
+
commit.events.each { |event| @event_buffer.push event.to_hash }
|
30
|
+
|
31
|
+
@event_store.mark_commit_as_dispatched commit
|
32
|
+
end
|
33
|
+
end while !commits.empty?
|
34
|
+
rescue StandardError => e
|
35
|
+
DaemonKit::logger.error "#{e}\n#{e.backtrace}"
|
36
|
+
end
|
37
|
+
|
38
|
+
EM.add_timer(wait_time) { dispatch_loop.call } unless @stop
|
39
|
+
end
|
40
|
+
|
41
|
+
EM.add_timer(wait_time) { dispatch_loop.call }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|