zookeeper 0.9.3-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/.gitignore +10 -0
- data/CHANGELOG +119 -0
- data/Gemfile +17 -0
- data/LICENSE +23 -0
- data/Manifest +29 -0
- data/README.markdown +59 -0
- data/Rakefile +139 -0
- data/examples/cloud_config.rb +125 -0
- data/ext/.gitignore +6 -0
- data/ext/Rakefile +51 -0
- data/ext/c_zookeeper.rb +212 -0
- data/ext/dbg.h +53 -0
- data/ext/depend +5 -0
- data/ext/extconf.rb +85 -0
- data/ext/generate_gvl_code.rb +316 -0
- data/ext/zkc-3.3.5.tar.gz +0 -0
- data/ext/zkrb_wrapper.c +731 -0
- data/ext/zkrb_wrapper.h +330 -0
- data/ext/zkrb_wrapper_compat.c +15 -0
- data/ext/zkrb_wrapper_compat.h +11 -0
- data/ext/zookeeper_base.rb +211 -0
- data/ext/zookeeper_c.c +725 -0
- data/ext/zookeeper_lib.c +677 -0
- data/ext/zookeeper_lib.h +172 -0
- data/java/zookeeper_base.rb +477 -0
- data/lib/zookeeper.rb +297 -0
- data/lib/zookeeper/acls.rb +40 -0
- data/lib/zookeeper/callbacks.rb +91 -0
- data/lib/zookeeper/common.rb +174 -0
- data/lib/zookeeper/common/queue_with_pipe.rb +78 -0
- data/lib/zookeeper/constants.rb +57 -0
- data/lib/zookeeper/em_client.rb +55 -0
- data/lib/zookeeper/exceptions.rb +100 -0
- data/lib/zookeeper/stat.rb +21 -0
- data/lib/zookeeper/version.rb +6 -0
- data/notes.txt +14 -0
- data/spec/c_zookeeper_spec.rb +50 -0
- data/spec/chrooted_connection_spec.rb +81 -0
- data/spec/default_watcher_spec.rb +41 -0
- data/spec/em_spec.rb +51 -0
- data/spec/log4j.properties +17 -0
- data/spec/shared/all_success_return_values.rb +10 -0
- data/spec/shared/connection_examples.rb +1018 -0
- data/spec/spec_helper.rb +119 -0
- data/spec/support/progress_formatter.rb +15 -0
- data/spec/zookeeper_spec.rb +24 -0
- data/test/test_basic.rb +37 -0
- data/test/test_callback1.rb +36 -0
- data/test/test_close.rb +16 -0
- data/test/test_esoteric.rb +7 -0
- data/test/test_watcher1.rb +56 -0
- data/test/test_watcher2.rb +52 -0
- metadata +181 -0
data/lib/zookeeper.rb
ADDED
@@ -0,0 +1,297 @@
|
|
1
|
+
# Ruby wrapper for the Zookeeper C API
|
2
|
+
|
3
|
+
require 'thread'
|
4
|
+
require 'monitor'
|
5
|
+
require 'forwardable'
|
6
|
+
require 'logger'
|
7
|
+
|
8
|
+
require 'zookeeper/common'
|
9
|
+
require 'zookeeper/constants'
|
10
|
+
require 'zookeeper/callbacks'
|
11
|
+
require 'zookeeper/exceptions'
|
12
|
+
require 'zookeeper/stat'
|
13
|
+
require 'zookeeper/acls'
|
14
|
+
|
15
|
+
|
16
|
+
if defined?(::JRUBY_VERSION)
|
17
|
+
$LOAD_PATH.unshift(File.expand_path('../java', File.dirname(__FILE__))).uniq!
|
18
|
+
else
|
19
|
+
$LOAD_PATH.unshift(File.expand_path('../ext', File.dirname(__FILE__))).uniq!
|
20
|
+
end
|
21
|
+
|
22
|
+
require 'zookeeper_base'
|
23
|
+
|
24
|
+
class Zookeeper < ZookeeperBase
|
25
|
+
unless defined?(@@logger)
|
26
|
+
@@logger = Logger.new($stderr).tap { |l| l.level = Logger::ERROR }
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.logger
|
30
|
+
@@logger
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.logger=(logger)
|
34
|
+
@@logger = logger
|
35
|
+
end
|
36
|
+
|
37
|
+
def reopen(timeout=10, watcher=nil)
|
38
|
+
warn "WARN: ZookeeperBase#reopen watcher argument is now ignored" if watcher
|
39
|
+
super
|
40
|
+
end
|
41
|
+
|
42
|
+
def initialize(host, timeout=10, watcher=nil)
|
43
|
+
super
|
44
|
+
end
|
45
|
+
|
46
|
+
def get(options = {})
|
47
|
+
assert_open
|
48
|
+
assert_supported_keys(options, [:path, :watcher, :watcher_context, :callback, :callback_context])
|
49
|
+
assert_required_keys(options, [:path])
|
50
|
+
|
51
|
+
req_id = setup_call(:get, options)
|
52
|
+
rc, value, stat = super(req_id, options[:path], options[:callback], options[:watcher])
|
53
|
+
|
54
|
+
rv = { :req_id => req_id, :rc => rc }
|
55
|
+
options[:callback] ? rv : rv.merge(:data => value, :stat => Stat.new(stat))
|
56
|
+
end
|
57
|
+
|
58
|
+
def set(options = {})
|
59
|
+
assert_open
|
60
|
+
assert_supported_keys(options, [:path, :data, :version, :callback, :callback_context])
|
61
|
+
assert_required_keys(options, [:path])
|
62
|
+
assert_valid_data_size!(options[:data])
|
63
|
+
options[:version] ||= -1
|
64
|
+
|
65
|
+
req_id = setup_call(:set, options)
|
66
|
+
rc, stat = super(req_id, options[:path], options[:data], options[:callback], options[:version])
|
67
|
+
|
68
|
+
rv = { :req_id => req_id, :rc => rc }
|
69
|
+
options[:callback] ? rv : rv.merge(:stat => Stat.new(stat))
|
70
|
+
end
|
71
|
+
|
72
|
+
def get_children(options = {})
|
73
|
+
assert_open
|
74
|
+
assert_supported_keys(options, [:path, :callback, :callback_context, :watcher, :watcher_context])
|
75
|
+
assert_required_keys(options, [:path])
|
76
|
+
|
77
|
+
req_id = setup_call(:get_children, options)
|
78
|
+
rc, children, stat = super(req_id, options[:path], options[:callback], options[:watcher])
|
79
|
+
|
80
|
+
rv = { :req_id => req_id, :rc => rc }
|
81
|
+
options[:callback] ? rv : rv.merge(:children => children, :stat => Stat.new(stat))
|
82
|
+
end
|
83
|
+
|
84
|
+
def stat(options = {})
|
85
|
+
assert_open
|
86
|
+
assert_supported_keys(options, [:path, :callback, :callback_context, :watcher, :watcher_context])
|
87
|
+
assert_required_keys(options, [:path])
|
88
|
+
|
89
|
+
req_id = setup_call(:stat, options)
|
90
|
+
rc, stat = exists(req_id, options[:path], options[:callback], options[:watcher])
|
91
|
+
|
92
|
+
rv = { :req_id => req_id, :rc => rc }
|
93
|
+
options[:callback] ? rv : rv.merge(:stat => Stat.new(stat))
|
94
|
+
end
|
95
|
+
|
96
|
+
def create(options = {})
|
97
|
+
assert_open
|
98
|
+
assert_supported_keys(options, [:path, :data, :acl, :ephemeral, :sequence, :callback, :callback_context])
|
99
|
+
assert_required_keys(options, [:path])
|
100
|
+
assert_valid_data_size!(options[:data])
|
101
|
+
|
102
|
+
flags = 0
|
103
|
+
flags |= ZOO_EPHEMERAL if options[:ephemeral]
|
104
|
+
flags |= ZOO_SEQUENCE if options[:sequence]
|
105
|
+
|
106
|
+
options[:acl] ||= ZOO_OPEN_ACL_UNSAFE
|
107
|
+
|
108
|
+
req_id = setup_call(:create, options)
|
109
|
+
rc, newpath = super(req_id, options[:path], options[:data], options[:callback], options[:acl], flags)
|
110
|
+
|
111
|
+
rv = { :req_id => req_id, :rc => rc }
|
112
|
+
options[:callback] ? rv : rv.merge(:path => newpath)
|
113
|
+
end
|
114
|
+
|
115
|
+
def delete(options = {})
|
116
|
+
assert_open
|
117
|
+
assert_supported_keys(options, [:path, :version, :callback, :callback_context])
|
118
|
+
assert_required_keys(options, [:path])
|
119
|
+
options[:version] ||= -1
|
120
|
+
|
121
|
+
req_id = setup_call(:delete, options)
|
122
|
+
rc = super(req_id, options[:path], options[:version], options[:callback])
|
123
|
+
|
124
|
+
{ :req_id => req_id, :rc => rc }
|
125
|
+
end
|
126
|
+
|
127
|
+
# this method is *only* asynchronous
|
128
|
+
#
|
129
|
+
# @note There is a discrepancy between the zkc and java versions. zkc takes
|
130
|
+
# a string_callback_t, java takes a VoidCallback. You should most likely use
|
131
|
+
# the ZookeeperCallbacks::VoidCallback and not rely on the string value.
|
132
|
+
#
|
133
|
+
def sync(options = {})
|
134
|
+
assert_open
|
135
|
+
assert_supported_keys(options, [:path, :callback, :callback_context])
|
136
|
+
assert_required_keys(options, [:path, :callback])
|
137
|
+
|
138
|
+
req_id = setup_call(:sync, options)
|
139
|
+
|
140
|
+
rc = super(req_id, options[:path]) # we don't pass options[:callback] here as this method is *always* async
|
141
|
+
|
142
|
+
{ :req_id => req_id, :rc => rc }
|
143
|
+
end
|
144
|
+
|
145
|
+
def set_acl(options = {})
|
146
|
+
assert_open
|
147
|
+
assert_supported_keys(options, [:path, :acl, :version, :callback, :callback_context])
|
148
|
+
assert_required_keys(options, [:path, :acl])
|
149
|
+
options[:version] ||= -1
|
150
|
+
|
151
|
+
req_id = setup_call(:set_acl, options)
|
152
|
+
rc = super(req_id, options[:path], options[:acl], options[:callback], options[:version])
|
153
|
+
|
154
|
+
{ :req_id => req_id, :rc => rc }
|
155
|
+
end
|
156
|
+
|
157
|
+
def get_acl(options = {})
|
158
|
+
assert_open
|
159
|
+
assert_supported_keys(options, [:path, :callback, :callback_context])
|
160
|
+
assert_required_keys(options, [:path])
|
161
|
+
|
162
|
+
req_id = setup_call(:get_acl, options)
|
163
|
+
rc, acls, stat = super(req_id, options[:path], options[:callback])
|
164
|
+
|
165
|
+
rv = { :req_id => req_id, :rc => rc }
|
166
|
+
options[:callback] ? rv : rv.merge(:acl => acls, :stat => Stat.new(stat))
|
167
|
+
end
|
168
|
+
|
169
|
+
# close this client and any underyling connections
|
170
|
+
def close
|
171
|
+
super
|
172
|
+
end
|
173
|
+
|
174
|
+
def state
|
175
|
+
super
|
176
|
+
end
|
177
|
+
|
178
|
+
def connected?
|
179
|
+
super
|
180
|
+
end
|
181
|
+
|
182
|
+
def connecting?
|
183
|
+
super
|
184
|
+
end
|
185
|
+
|
186
|
+
def associating?
|
187
|
+
super
|
188
|
+
end
|
189
|
+
|
190
|
+
# There are some operations that are dangerous in the context of the event
|
191
|
+
# dispatch thread (because they would block further event delivery). This
|
192
|
+
# method allows clients to know if they're currently executing in the context of an
|
193
|
+
# event.
|
194
|
+
#
|
195
|
+
# @returns [true,false] true if the current thread is the event dispatch thread
|
196
|
+
def event_dispatch_thread?
|
197
|
+
super
|
198
|
+
end
|
199
|
+
|
200
|
+
# for expert use only. set the underlying debug level for the C layer, has no
|
201
|
+
# effect in java
|
202
|
+
#
|
203
|
+
# @private
|
204
|
+
def self.set_debug_level(val)
|
205
|
+
if defined?(::CZookeeper)
|
206
|
+
CZookeeper.set_debug_level(val.to_i)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
# @private
|
211
|
+
def self.get_debug_level
|
212
|
+
if defined?(::CZookeeper)
|
213
|
+
CZookeeper.get_debug_level
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
class << self
|
218
|
+
# @private
|
219
|
+
alias :debug_level= :set_debug_level
|
220
|
+
|
221
|
+
# @private
|
222
|
+
alias :debug_level :get_debug_level
|
223
|
+
end
|
224
|
+
|
225
|
+
# DEPRECATED: use the class-level method instead
|
226
|
+
def set_debug_level(val)
|
227
|
+
super
|
228
|
+
end
|
229
|
+
|
230
|
+
# has the underlying connection been closed?
|
231
|
+
def closed?
|
232
|
+
super
|
233
|
+
end
|
234
|
+
|
235
|
+
# is the event delivery system running?
|
236
|
+
def running?
|
237
|
+
super
|
238
|
+
end
|
239
|
+
|
240
|
+
# return the session id of the current connection as an Fixnum
|
241
|
+
def session_id
|
242
|
+
super
|
243
|
+
end
|
244
|
+
|
245
|
+
# Return the passwd portion of this connection's credentials as a String
|
246
|
+
def session_passwd
|
247
|
+
super
|
248
|
+
end
|
249
|
+
|
250
|
+
protected
|
251
|
+
# used during shutdown, awaken the event delivery thread if it's blocked
|
252
|
+
# waiting for the next event
|
253
|
+
def wake_event_loop!
|
254
|
+
super
|
255
|
+
end
|
256
|
+
|
257
|
+
# starts the event delivery subsystem going. after calling this method, running? will be true
|
258
|
+
def setup_dispatch_thread!
|
259
|
+
super
|
260
|
+
end
|
261
|
+
|
262
|
+
# TODO: describe what this does
|
263
|
+
def get_default_global_watcher
|
264
|
+
super
|
265
|
+
end
|
266
|
+
|
267
|
+
def logger
|
268
|
+
Zookeeper.logger
|
269
|
+
end
|
270
|
+
|
271
|
+
def assert_valid_data_size!(data)
|
272
|
+
return if data.nil?
|
273
|
+
|
274
|
+
data = data.to_s
|
275
|
+
if data.length >= 1048576 # one megabyte
|
276
|
+
raise ZookeeperException::DataTooLargeException, "data must be smaller than 1 MiB, your data starts with: #{data[0..32].inspect}"
|
277
|
+
end
|
278
|
+
nil
|
279
|
+
end
|
280
|
+
|
281
|
+
private
|
282
|
+
# TODO: Sanitize user mistakes by unregistering watchers from ops that
|
283
|
+
# don't return ZOK (except wexists)? Make users clean up after themselves for now.
|
284
|
+
#
|
285
|
+
# XXX: is this dead code?
|
286
|
+
def unregister_watcher(req_id)
|
287
|
+
@mutex.synchronize {
|
288
|
+
@watcher_reqs.delete(req_id)
|
289
|
+
}
|
290
|
+
end
|
291
|
+
|
292
|
+
# must be supplied by parent class impl.
|
293
|
+
def assert_open
|
294
|
+
super
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module ZookeeperACLs
|
2
|
+
class Id
|
3
|
+
attr_reader :scheme, :id
|
4
|
+
def initialize(hash)
|
5
|
+
@scheme = hash[:scheme]
|
6
|
+
@id = hash[:id]
|
7
|
+
end
|
8
|
+
|
9
|
+
def to_hash
|
10
|
+
{ :id => id, :scheme => scheme }
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class ACL
|
15
|
+
attr_reader :perms, :id
|
16
|
+
def initialize(hash)
|
17
|
+
@perms = hash[:perms]
|
18
|
+
v = hash[:id]
|
19
|
+
@id = v.kind_of?(Hash) ? Id.new(v) : v
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_hash
|
23
|
+
{ :perms => perms, :id => id.to_hash }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
ZOO_PERM_READ = 0
|
28
|
+
ZOO_PERM_WRITE = 1
|
29
|
+
ZOO_PERM_CREATE = 2
|
30
|
+
ZOO_PERM_DELETE = 4
|
31
|
+
ZOO_PERM_ADMIN = 8
|
32
|
+
ZOO_PERM_ALL = ZOO_PERM_READ | ZOO_PERM_WRITE | ZOO_PERM_CREATE | ZOO_PERM_DELETE | ZOO_PERM_ADMIN
|
33
|
+
|
34
|
+
ZOO_ANYONE_ID_UNSAFE = Id.new(:scheme => "world", :id => "anyone")
|
35
|
+
ZOO_AUTH_IDS = Id.new(:scheme => "auth", :id => "")
|
36
|
+
|
37
|
+
ZOO_OPEN_ACL_UNSAFE = [ACL.new(:perms => ZOO_PERM_ALL, :id => ZOO_ANYONE_ID_UNSAFE)]
|
38
|
+
ZOO_READ_ACL_UNSAFE = [ACL.new(:perms => ZOO_PERM_READ, :id => ZOO_ANYONE_ID_UNSAFE)]
|
39
|
+
ZOO_CREATOR_ALL_ACL = [ACL.new(:perms => ZOO_PERM_ALL, :id => ZOO_AUTH_IDS)]
|
40
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
module ZookeeperCallbacks
|
2
|
+
class Callback
|
3
|
+
attr_reader :proc, :completed, :context
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@completed = false
|
7
|
+
@proc = Proc.new do |hash|
|
8
|
+
initialize_context(hash)
|
9
|
+
yield if block_given?
|
10
|
+
@completed = true
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def call(*args)
|
15
|
+
# puts "call passed #{args.inspect}"
|
16
|
+
@proc.call(*args)
|
17
|
+
end
|
18
|
+
|
19
|
+
def completed?
|
20
|
+
@completed
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize_context(hash)
|
24
|
+
@context = nil
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class WatcherCallback < Callback
|
29
|
+
## wexists, awexists, wget, awget, wget_children, awget_children
|
30
|
+
attr_reader :type, :state, :path
|
31
|
+
|
32
|
+
def initialize_context(hash)
|
33
|
+
@type, @state, @path, @context = hash[:type], hash[:state], hash[:path], hash[:context]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class DataCallback < Callback
|
38
|
+
## aget, awget
|
39
|
+
attr_reader :return_code, :data, :stat
|
40
|
+
|
41
|
+
def initialize_context(hash)
|
42
|
+
@return_code, @data, @stat, @context = hash[:rc], hash[:data], hash[:stat], hash[:context]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class StringCallback < Callback
|
47
|
+
## acreate, async
|
48
|
+
attr_reader :return_code, :string, :context
|
49
|
+
|
50
|
+
alias path string
|
51
|
+
|
52
|
+
def initialize_context(hash)
|
53
|
+
@return_code, @string, @context = hash[:rc], hash[:string], hash[:context]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
class StringsCallback < Callback
|
58
|
+
## aget_children, awget_children
|
59
|
+
attr_reader :return_code, :children, :stat
|
60
|
+
|
61
|
+
def initialize_context(hash)
|
62
|
+
@return_code, @children, @stat, @context = hash[:rc], hash[:strings], hash[:stat], hash[:context]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
class StatCallback < Callback
|
67
|
+
## aset, aexists, awexists
|
68
|
+
attr_reader :return_code, :stat
|
69
|
+
|
70
|
+
def initialize_context(hash)
|
71
|
+
@return_code, @stat, @context = hash[:rc], hash[:stat], hash[:context]
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class VoidCallback < Callback
|
76
|
+
## adelete, aset_acl, add_auth
|
77
|
+
attr_reader :return_code
|
78
|
+
|
79
|
+
def initialize_context(hash)
|
80
|
+
@return_code, @context = hash[:rc], hash[:context]
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
class ACLCallback < Callback
|
85
|
+
## aget_acl
|
86
|
+
attr_reader :return_code, :acl, :stat
|
87
|
+
def initialize_context(hash)
|
88
|
+
@return_code, @acl, @stat, @context = hash[:rc], hash[:acl], hash[:stat], hash[:context]
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,174 @@
|
|
1
|
+
require 'zookeeper/exceptions'
|
2
|
+
|
3
|
+
module ZookeeperCommon
|
4
|
+
# sigh, i guess define this here?
|
5
|
+
ZKRB_GLOBAL_CB_REQ = -1
|
6
|
+
|
7
|
+
def event_dispatch_thread?
|
8
|
+
@dispatcher && (@dispatcher == Thread.current)
|
9
|
+
end
|
10
|
+
|
11
|
+
protected
|
12
|
+
def get_next_event(blocking=true)
|
13
|
+
@event_queue.pop(!blocking).tap do |event|
|
14
|
+
logger.debug { "#{self.class}##{__method__} delivering event #{event.inspect}" }
|
15
|
+
end
|
16
|
+
rescue ThreadError
|
17
|
+
nil
|
18
|
+
end
|
19
|
+
|
20
|
+
def setup_call(meth_name, opts)
|
21
|
+
req_id = nil
|
22
|
+
@mutex.synchronize {
|
23
|
+
req_id = @current_req_id
|
24
|
+
@current_req_id += 1
|
25
|
+
setup_completion(req_id, meth_name, opts) if opts[:callback]
|
26
|
+
setup_watcher(req_id, opts) if opts[:watcher]
|
27
|
+
}
|
28
|
+
req_id
|
29
|
+
end
|
30
|
+
|
31
|
+
def setup_watcher(req_id, call_opts)
|
32
|
+
@watcher_reqs[req_id] = { :watcher => call_opts[:watcher],
|
33
|
+
:context => call_opts[:watcher_context] }
|
34
|
+
end
|
35
|
+
|
36
|
+
# as a hack, to provide consistency between the java implementation and the C
|
37
|
+
# implementation when dealing w/ chrooted connections, we override this in
|
38
|
+
# ext/zookeeper_base.rb to wrap the callback in a chroot-path-stripping block.
|
39
|
+
#
|
40
|
+
# we don't use meth_name here, but we need it in the C implementation
|
41
|
+
#
|
42
|
+
def setup_completion(req_id, meth_name, call_opts)
|
43
|
+
@completion_reqs[req_id] = { :callback => call_opts[:callback],
|
44
|
+
:context => call_opts[:callback_context] }
|
45
|
+
end
|
46
|
+
|
47
|
+
def get_watcher(req_id)
|
48
|
+
@mutex.synchronize {
|
49
|
+
(req_id == ZKRB_GLOBAL_CB_REQ) ? @watcher_reqs[req_id] : @watcher_reqs.delete(req_id)
|
50
|
+
}
|
51
|
+
end
|
52
|
+
|
53
|
+
def get_completion(req_id)
|
54
|
+
@mutex.synchronize { @completion_reqs.delete(req_id) }
|
55
|
+
end
|
56
|
+
|
57
|
+
def setup_dispatch_thread!
|
58
|
+
logger.debug { "starting dispatch thread" }
|
59
|
+
@dispatcher ||= Thread.new do
|
60
|
+
while true
|
61
|
+
begin
|
62
|
+
dispatch_next_callback(get_next_event(true))
|
63
|
+
rescue QueueWithPipe::ShutdownException
|
64
|
+
logger.info { "dispatch thread exiting, got shutdown exception" }
|
65
|
+
break
|
66
|
+
rescue Exception => e
|
67
|
+
$stderr.puts ["#{e.class}: #{e.message}", e.backtrace.map { |n| "\t#{n}" }.join("\n")].join("\n")
|
68
|
+
end
|
69
|
+
end
|
70
|
+
signal_dispatch_thread_exit!
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# this method is part of the reopen/close code, and is responsible for
|
75
|
+
# shutting down the dispatch thread.
|
76
|
+
#
|
77
|
+
# @dispatcher will be nil when this method exits
|
78
|
+
#
|
79
|
+
def stop_dispatch_thread!
|
80
|
+
logger.debug { "#{self.class}##{__method__}" }
|
81
|
+
|
82
|
+
if @dispatcher
|
83
|
+
if @dispatcher.join(0)
|
84
|
+
@dispatcher = nil
|
85
|
+
return
|
86
|
+
end
|
87
|
+
|
88
|
+
@mutex.synchronize do
|
89
|
+
event_queue.graceful_close!
|
90
|
+
|
91
|
+
# we now release the mutex so that dispatch_next_callback can grab it
|
92
|
+
# to do what it needs to do while delivering events
|
93
|
+
#
|
94
|
+
# wait for a maximum of 2 sec for dispatcher to signal exit (should be
|
95
|
+
# fast)
|
96
|
+
@dispatch_shutdown_cond.wait(2)
|
97
|
+
|
98
|
+
# wait for another 2 sec for the thread to join
|
99
|
+
unless @dispatcher.join(2)
|
100
|
+
logger.error { "Dispatch thread did not join cleanly, continuing" }
|
101
|
+
end
|
102
|
+
@dispatcher = nil
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def signal_dispatch_thread_exit!
|
108
|
+
@mutex.synchronize do
|
109
|
+
logger.debug { "dispatch thread exiting!" }
|
110
|
+
@dispatch_shutdown_cond.broadcast
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def dispatch_next_callback(hash)
|
115
|
+
return nil unless hash
|
116
|
+
|
117
|
+
Zookeeper.logger.debug { "get_next_event returned: #{prettify_event(hash).inspect}" }
|
118
|
+
|
119
|
+
is_completion = hash.has_key?(:rc)
|
120
|
+
|
121
|
+
hash[:stat] = ZookeeperStat::Stat.new(hash[:stat]) if hash.has_key?(:stat)
|
122
|
+
hash[:acl] = hash[:acl].map { |acl| ZookeeperACLs::ACL.new(acl) } if hash[:acl]
|
123
|
+
|
124
|
+
callback_context = is_completion ? get_completion(hash[:req_id]) : get_watcher(hash[:req_id])
|
125
|
+
|
126
|
+
# When connectivity to the server has been lost (as indicated by SESSION_EVENT)
|
127
|
+
# we want to rerun the callback at a later time when we eventually do have
|
128
|
+
# a valid response.
|
129
|
+
if hash[:type] == ZookeeperConstants::ZOO_SESSION_EVENT
|
130
|
+
is_completion ? setup_completion(hash[:req_id], callback_context) : setup_watcher(hash[:req_id], callback_context)
|
131
|
+
end
|
132
|
+
if callback_context
|
133
|
+
callback = is_completion ? callback_context[:callback] : callback_context[:watcher]
|
134
|
+
|
135
|
+
hash[:context] = callback_context[:context]
|
136
|
+
|
137
|
+
# TODO: Eventually enforce derivation from Zookeeper::Callback
|
138
|
+
if callback.respond_to?(:call)
|
139
|
+
callback.call(hash)
|
140
|
+
else
|
141
|
+
# puts "dispatch_next_callback found non-callback => #{callback.inspect}"
|
142
|
+
end
|
143
|
+
else
|
144
|
+
logger.warn { "Duplicate event received (no handler for req_id #{hash[:req_id]}, event: #{hash.inspect}" }
|
145
|
+
end
|
146
|
+
true
|
147
|
+
end
|
148
|
+
|
149
|
+
def assert_supported_keys(args, supported)
|
150
|
+
unless (args.keys - supported).empty?
|
151
|
+
raise ZookeeperExceptions::ZookeeperException::BadArguments, # this heirarchy is kind of retarded
|
152
|
+
"Supported arguments are: #{supported.inspect}, but arguments #{args.keys.inspect} were supplied instead"
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def assert_required_keys(args, required)
|
157
|
+
unless (required - args.keys).empty?
|
158
|
+
raise ZookeeperExceptions::ZookeeperException::BadArguments,
|
159
|
+
"Required arguments are: #{required.inspect}, but only the arguments #{args.keys.inspect} were supplied."
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
private
|
164
|
+
def prettify_event(hash)
|
165
|
+
hash.dup.tap do |h|
|
166
|
+
# pretty up the event display
|
167
|
+
h[:type] = ZookeeperConstants::EVENT_TYPE_NAMES.fetch(h[:type]) if h[:type]
|
168
|
+
h[:state] = ZookeeperConstants::STATE_NAMES.fetch(h[:state]) if h[:state]
|
169
|
+
h[:req_id] = :global_session if h[:req_id] == -1
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
require 'zookeeper/common/queue_with_pipe'
|