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.
Files changed (32) hide show
  1. data/Gemfile +2 -0
  2. data/Gemfile.lock +103 -0
  3. data/Rakefile +182 -0
  4. data/euston-rabbitmq.gemspec +74 -0
  5. data/lib/euston-rabbitmq/buffered_message_dispatcher.rb +48 -0
  6. data/lib/euston-rabbitmq/command_handler_bindings.rb +30 -0
  7. data/lib/euston-rabbitmq/command_handlers/retry_failed_message.rb +29 -0
  8. data/lib/euston-rabbitmq/errors.rb +5 -0
  9. data/lib/euston-rabbitmq/event_handler_binding.rb +114 -0
  10. data/lib/euston-rabbitmq/event_handler_bindings.rb +30 -0
  11. data/lib/euston-rabbitmq/event_handlers/message_failure.rb +27 -0
  12. data/lib/euston-rabbitmq/event_store_dispatcher.rb +45 -0
  13. data/lib/euston-rabbitmq/exchanges.rb +17 -0
  14. data/lib/euston-rabbitmq/handler_bindings.rb +115 -0
  15. data/lib/euston-rabbitmq/message_buffer.rb +68 -0
  16. data/lib/euston-rabbitmq/message_logger.rb +50 -0
  17. data/lib/euston-rabbitmq/queue.rb +30 -0
  18. data/lib/euston-rabbitmq/queues.rb +13 -0
  19. data/lib/euston-rabbitmq/read_model/failed_message.rb +36 -0
  20. data/lib/euston-rabbitmq/read_model/message_buffer.rb +52 -0
  21. data/lib/euston-rabbitmq/read_model/message_log.rb +37 -0
  22. data/lib/euston-rabbitmq/version.rb +5 -0
  23. data/lib/euston-rabbitmq.rb +28 -0
  24. data/spec/command_buffer_spec.rb +69 -0
  25. data/spec/event_buffer_spec.rb +69 -0
  26. data/spec/exchange_declaration_spec.rb +28 -0
  27. data/spec/message_failure_spec.rb +77 -0
  28. data/spec/mt_safe_queue_subscription_spec.rb +72 -0
  29. data/spec/safe_queue_subscription_spec.rb +50 -0
  30. data/spec/spec_helper.rb +65 -0
  31. data/spec/support/factories.rb +18 -0
  32. metadata +233 -0
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source :rubygems
2
+ gemspec
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,5 @@
1
+ module Euston
2
+ module RabbitMq
3
+ class MessageDecodeFailedError < StandardError; end
4
+ end
5
+ 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