cf-message-bus 0.0.1
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.
@@ -0,0 +1,121 @@
|
|
1
|
+
require "eventmachine"
|
2
|
+
require "eventmachine/schedule_sync"
|
3
|
+
require "cf_message_bus/message_bus_factory"
|
4
|
+
|
5
|
+
module CfMessageBus
|
6
|
+
class Error < StandardError; end
|
7
|
+
|
8
|
+
class MessageBus
|
9
|
+
def initialize(config)
|
10
|
+
@logger = config[:logger]
|
11
|
+
@internal_bus = MessageBusFactory.message_bus(config[:uri])
|
12
|
+
@subscriptions = {}
|
13
|
+
@internal_bus.on_reconnect { start_internal_bus_recovery }
|
14
|
+
@recovery_callback = lambda {}
|
15
|
+
end
|
16
|
+
|
17
|
+
def subscribe(subject, opts = {}, &block)
|
18
|
+
@subscriptions[subject] = [opts, block]
|
19
|
+
|
20
|
+
subscribe_on_reactor(subject, opts) do |parsed_data, inbox|
|
21
|
+
EM.defer do
|
22
|
+
run_handler(block, parsed_data, inbox, subject, 'subscription')
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def publish(subject, message = nil, &callback)
|
28
|
+
EM.schedule do
|
29
|
+
internal_bus.publish(subject, encode(message), &callback)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def recover(&block)
|
34
|
+
@recovery_callback = block
|
35
|
+
end
|
36
|
+
|
37
|
+
def request(subject, data = nil, options = {}, &block)
|
38
|
+
internal_bus.request(subject, encode(data), options) do |payload, inbox|
|
39
|
+
process_message(payload, inbox) do |parsed_data, inbox|
|
40
|
+
run_handler(block, parsed_data, inbox, subject, 'response')
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def synchronous_request(subject, data = nil, opts = {})
|
46
|
+
result_count = opts[:result_count] || 1
|
47
|
+
timeout = opts[:timeout] || -1
|
48
|
+
|
49
|
+
return [] if result_count <= 0
|
50
|
+
|
51
|
+
response = EM.schedule_sync do |promise|
|
52
|
+
results = []
|
53
|
+
|
54
|
+
sid = request(subject, encode(data), max: result_count) do |data|
|
55
|
+
results << data
|
56
|
+
promise.deliver(results) if results.size == result_count
|
57
|
+
end
|
58
|
+
|
59
|
+
if timeout >= 0
|
60
|
+
internal_bus.timeout(sid, timeout, expected: result_count) do
|
61
|
+
promise.deliver(results)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
response
|
67
|
+
end
|
68
|
+
|
69
|
+
def unsubscribe(subscription_id)
|
70
|
+
internal_bus.unsubscribe(subscription_id)
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
attr_reader :internal_bus
|
76
|
+
|
77
|
+
def run_handler(block, parsed_data, inbox, subject, type)
|
78
|
+
begin
|
79
|
+
block.yield(parsed_data, inbox)
|
80
|
+
rescue => e
|
81
|
+
@logger.error "exception processing #{type} for: '#{subject}' '#{parsed_data.inspect}' \n#{e.inspect}\n #{e.backtrace.join("\n")}"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def start_internal_bus_recovery
|
86
|
+
EM.defer do
|
87
|
+
@logger.info("Reconnected to internal_bus.")
|
88
|
+
|
89
|
+
@recovery_callback.call
|
90
|
+
|
91
|
+
@subscriptions.each do |subject, options|
|
92
|
+
@logger.info("Resubscribing to #{subject}")
|
93
|
+
subscribe(subject, options[0], &options[1])
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def subscribe_on_reactor(subject, opts = {}, &blk)
|
99
|
+
EM.schedule do
|
100
|
+
internal_bus.subscribe(subject, opts) do |msg, inbox|
|
101
|
+
process_message(msg, inbox, &blk)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def process_message(msg, inbox, &block)
|
107
|
+
payload = JSON.parse(msg, symbolize_keys: true)
|
108
|
+
block.yield(payload, inbox)
|
109
|
+
rescue => e
|
110
|
+
@logger.error "exception parsing json: '#{msg}' '#{e.inspect}'"
|
111
|
+
block.yield({error: "JSON Parse Error: failed to parse", exception: e, message: msg}, inbox)
|
112
|
+
end
|
113
|
+
|
114
|
+
def encode(message)
|
115
|
+
unless message.nil? || message.is_a?(String)
|
116
|
+
message = JSON.dump(message)
|
117
|
+
end
|
118
|
+
message
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module CfMessageBus
|
2
|
+
class MockMessageBus
|
3
|
+
def initialize(config = {})
|
4
|
+
@logger = config[:logger]
|
5
|
+
@subscriptions = Hash.new { |hash, key| hash[key] = [] }
|
6
|
+
@requests = {}
|
7
|
+
@published_messages = []
|
8
|
+
end
|
9
|
+
|
10
|
+
def subscribe(subject, opts = {}, &blk)
|
11
|
+
@subscriptions[subject] << blk
|
12
|
+
subject
|
13
|
+
end
|
14
|
+
|
15
|
+
def publish(subject, message = nil, &callback)
|
16
|
+
@subscriptions[subject].each do |subscription|
|
17
|
+
subscription.call(symbolize_keys(message))
|
18
|
+
end
|
19
|
+
|
20
|
+
@published_messages.push({subject: subject, message: message, callback: callback})
|
21
|
+
|
22
|
+
callback.call if callback
|
23
|
+
end
|
24
|
+
|
25
|
+
def request(subject, data=nil, options={}, &blk)
|
26
|
+
@requests[subject] = blk
|
27
|
+
publish(subject, data)
|
28
|
+
subject
|
29
|
+
end
|
30
|
+
|
31
|
+
def synchronous_request(subject, data=nil, options={})
|
32
|
+
end
|
33
|
+
|
34
|
+
def unsubscribe(subscription_id)
|
35
|
+
@subscriptions.delete(subscription_id)
|
36
|
+
@requests.delete(subscription_id)
|
37
|
+
end
|
38
|
+
|
39
|
+
def recover(&block)
|
40
|
+
@recovery = block
|
41
|
+
end
|
42
|
+
|
43
|
+
def respond_to_request(request_subject, data)
|
44
|
+
block = @requests.fetch(request_subject) { lambda { |data| nil } }
|
45
|
+
block.call(symbolize_keys(data))
|
46
|
+
end
|
47
|
+
|
48
|
+
def do_recovery
|
49
|
+
@recovery.call if @recovery
|
50
|
+
end
|
51
|
+
|
52
|
+
def published_messages
|
53
|
+
@published_messages
|
54
|
+
end
|
55
|
+
|
56
|
+
def clear_published_messages
|
57
|
+
@published_messages.clear
|
58
|
+
end
|
59
|
+
|
60
|
+
def has_published?(subject)
|
61
|
+
@published_messages.find { |message| message[:subject] == subject }
|
62
|
+
end
|
63
|
+
|
64
|
+
def has_published_with_message?(subject, message)
|
65
|
+
@published_messages.find { |publication| publication[:subject] == subject && publication[:message] == message }
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def symbolize_keys(hash)
|
71
|
+
return hash unless hash.is_a?(Hash)
|
72
|
+
hash.inject({}) do |memo, (key, value)|
|
73
|
+
memo[key.to_sym] = symbolize_keys(value)
|
74
|
+
memo
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require "vcap/concurrency"
|
2
|
+
|
3
|
+
module EventMachine
|
4
|
+
# Runs a block on the reactor thread and blocks the current thread
|
5
|
+
# while waiting for the result. If the block raises an exception,
|
6
|
+
# it will be re-thrown in the calling thread.
|
7
|
+
#
|
8
|
+
# @param [Block] blk The block to be executed on the reactor thread.
|
9
|
+
#
|
10
|
+
# @return [Object] The result of calling blk.
|
11
|
+
def self.schedule_sync(&blk)
|
12
|
+
promise = VCAP::Concurrency::Promise.new
|
13
|
+
EM.schedule do
|
14
|
+
begin
|
15
|
+
if blk.arity > 0
|
16
|
+
blk.call(promise)
|
17
|
+
else
|
18
|
+
promise.deliver(blk.call)
|
19
|
+
end
|
20
|
+
rescue Exception => e
|
21
|
+
promise.fail(e)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
promise.resolve
|
26
|
+
end
|
27
|
+
end
|
metadata
ADDED
@@ -0,0 +1,164 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cf-message-bus
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- CloudFoundry Core Team
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-07-16 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: bundler
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '1.3'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '1.3'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rspec
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: eventmachine
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 1.0.0
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 1.0.0
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: nats
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - '='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 0.4.26
|
70
|
+
type: :runtime
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - '='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 0.4.26
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: vcap-concurrency
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :runtime
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: yajl-ruby
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
type: :runtime
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: vcap-concurrency
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ! '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
description: Abstraction layer around NATS messaging bus
|
127
|
+
email:
|
128
|
+
- cfpi-dev@googlegroups.com
|
129
|
+
executables: []
|
130
|
+
extensions: []
|
131
|
+
extra_rdoc_files: []
|
132
|
+
files:
|
133
|
+
- lib/cf_message_bus/message_bus.rb
|
134
|
+
- lib/cf_message_bus/message_bus_factory.rb
|
135
|
+
- lib/cf_message_bus/mock_message_bus.rb
|
136
|
+
- lib/cf_message_bus/version.rb
|
137
|
+
- lib/cf_message_bus.rb
|
138
|
+
- lib/eventmachine/schedule_sync.rb
|
139
|
+
homepage:
|
140
|
+
licenses:
|
141
|
+
- Apache
|
142
|
+
post_install_message:
|
143
|
+
rdoc_options: []
|
144
|
+
require_paths:
|
145
|
+
- lib
|
146
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
147
|
+
none: false
|
148
|
+
requirements:
|
149
|
+
- - ! '>='
|
150
|
+
- !ruby/object:Gem::Version
|
151
|
+
version: '0'
|
152
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
153
|
+
none: false
|
154
|
+
requirements:
|
155
|
+
- - ! '>='
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: '0'
|
158
|
+
requirements: []
|
159
|
+
rubyforge_project:
|
160
|
+
rubygems_version: 1.8.23
|
161
|
+
signing_key:
|
162
|
+
specification_version: 3
|
163
|
+
summary: Cloud Foundry message bus
|
164
|
+
test_files: []
|