zk 0.6.5 → 0.7.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.
- data/.dotfiles/rspec-logging +4 -0
- data/.gitignore +2 -0
- data/.yardopts +8 -0
- data/Gemfile +6 -1
- data/README.markdown +86 -0
- data/lib/z_k/client/base.rb +692 -0
- data/lib/z_k/client/conveniences.rb +134 -0
- data/lib/z_k/client/state_mixin.rb +94 -0
- data/lib/z_k/client/unixisms.rb +89 -0
- data/lib/z_k/client.rb +12 -891
- data/lib/z_k/election.rb +3 -0
- data/lib/z_k/event_handler.rb +7 -5
- data/lib/z_k/mongoid.rb +1 -1
- data/lib/z_k/pool.rb +70 -27
- data/lib/z_k/threadpool.rb +7 -2
- data/lib/z_k/version.rb +1 -1
- data/lib/z_k.rb +1 -2
- data/spec/spec_helper.rb +1 -0
- data/spec/support/logging_progress_bar_formatter.rb +14 -0
- data/spec/watch_spec.rb +26 -8
- data/spec/{client_spec.rb → z_k/client_spec.rb} +1 -1
- data/spec/{election_spec.rb → z_k/election_spec.rb} +2 -3
- data/spec/{locker_spec.rb → z_k/locker_spec.rb} +1 -1
- data/spec/{mongoid_spec.rb → z_k/mongoid_spec.rb} +1 -1
- data/spec/{client_pool_spec.rb → z_k/pool_spec.rb} +98 -126
- data/spec/{threadpool_spec.rb → z_k/threadpool_spec.rb} +6 -3
- data/zk.gemspec +1 -0
- metadata +37 -26
@@ -0,0 +1,692 @@
|
|
1
|
+
module ZK
|
2
|
+
module Client
|
3
|
+
class Base
|
4
|
+
include StateMixin
|
5
|
+
include Unixisms
|
6
|
+
include Conveniences
|
7
|
+
|
8
|
+
attr_reader :event_handler
|
9
|
+
|
10
|
+
# @private the wrapped connection object
|
11
|
+
attr_reader :cnx
|
12
|
+
|
13
|
+
|
14
|
+
# Create a new client and connect to the zookeeper server.
|
15
|
+
#
|
16
|
+
# +host+ should be a string of comma-separated host:port pairs. You can
|
17
|
+
# also supply an optional "chroot" suffix that will act as an implicit
|
18
|
+
# prefix to all paths supplied.
|
19
|
+
#
|
20
|
+
# example:
|
21
|
+
#
|
22
|
+
# ZK::Client.new("zk01:2181,zk02:2181/chroot/path")
|
23
|
+
#
|
24
|
+
def initialize(host, opts={})
|
25
|
+
@event_handler = EventHandler.new(self)
|
26
|
+
yield self if block_given?
|
27
|
+
@cnx = ::Zookeeper.new(host, DEFAULT_TIMEOUT, @event_handler.get_default_watcher_block)
|
28
|
+
@threadpool = Threadpool.new
|
29
|
+
end
|
30
|
+
|
31
|
+
# @deprecated: for backwards compatibility only
|
32
|
+
def watcher
|
33
|
+
event_handler
|
34
|
+
end
|
35
|
+
|
36
|
+
# returns true if the connection has been closed
|
37
|
+
#--
|
38
|
+
# XXX: should this be *our* idea of closed or ZOO_CLOSED_STATE ?
|
39
|
+
def closed?
|
40
|
+
defined?(::JRUBY_VERSION) ? jruby_closed? : mri_closed?
|
41
|
+
end
|
42
|
+
|
43
|
+
def inspect
|
44
|
+
"#<#{self.class.name}:#{object_id} ...>"
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
# @private
|
49
|
+
def jruby_closed?
|
50
|
+
@cnx.state == Java::OrgApacheZookeeper::ZooKeeper::States::CLOSED
|
51
|
+
end
|
52
|
+
|
53
|
+
# @private
|
54
|
+
def mri_closed?
|
55
|
+
@cnx.state or false
|
56
|
+
rescue RuntimeError => e
|
57
|
+
# gah, lame error parsing here
|
58
|
+
raise e if (e.message != 'zookeeper handle is closed') and not defined?(::JRUBY_VERSION)
|
59
|
+
true
|
60
|
+
end
|
61
|
+
|
62
|
+
public
|
63
|
+
|
64
|
+
# reopen the underlying connection
|
65
|
+
# returns state of connection after operation
|
66
|
+
def reopen(timeout=10)
|
67
|
+
@cnx.reopen(timeout, @event_handler.get_default_watcher_block)
|
68
|
+
@threadpool.start! # restart the threadpool if previously stopped by close!
|
69
|
+
state
|
70
|
+
end
|
71
|
+
|
72
|
+
# closes the underlying connection and deregisters all callbacks
|
73
|
+
def close!
|
74
|
+
@event_handler.clear!
|
75
|
+
wrap_state_closed_error { @cnx.close }
|
76
|
+
@threadpool.shutdown
|
77
|
+
nil
|
78
|
+
end
|
79
|
+
|
80
|
+
# Create a node with the given path. The node data will be the given data.
|
81
|
+
# The path is returned.
|
82
|
+
#
|
83
|
+
# If the ephemeral option is given, the znode creaed will be removed by the
|
84
|
+
# server automatically when the session associated with the creation of the
|
85
|
+
# node expires. Note that ephemeral nodes cannot have children.
|
86
|
+
#
|
87
|
+
# The sequence option, if true, will cause the server to create a sequential
|
88
|
+
# node. The actual path name of a sequential node will be the given path
|
89
|
+
# plus a suffix "_i" where i is the current sequential number of the node.
|
90
|
+
# Once such a node is created, the sequential number for the path will be
|
91
|
+
# incremented by one (i.e. the generated path will be unique across all
|
92
|
+
# clients).
|
93
|
+
#
|
94
|
+
# Note that since a different actual path is used for each invocation of
|
95
|
+
# creating sequential node with the same path argument, the call will never
|
96
|
+
# throw a NodeExists exception.
|
97
|
+
#
|
98
|
+
# @todo clean up the verbiage around watchers
|
99
|
+
#
|
100
|
+
# This operation, if successful, will trigger all the watches left on the
|
101
|
+
# node of the given path by exists and get API calls, and the watches left
|
102
|
+
# on the parent node by children API calls.
|
103
|
+
#
|
104
|
+
# If a node is created successfully, the ZooKeeper server will trigger the
|
105
|
+
# watches on the path left by exists calls, and the watches on the parent
|
106
|
+
# of the node by children calls.
|
107
|
+
#
|
108
|
+
# @param [String] path absolute path of the znode
|
109
|
+
# @param [String] data the data to create the znode with
|
110
|
+
#
|
111
|
+
# @option opts [Integer] :acl defaults to <tt>ZookeeperACLs::ZOO_OPEN_ACL_UNSAFE</tt>,
|
112
|
+
# otherwise the ACL for the node. Should be a +ZOO_*+ constant defined under the
|
113
|
+
# ZookeeperACLs module in the zookeeper gem.
|
114
|
+
#
|
115
|
+
# @option opts [bool] :ephemeral (false) if true, the created node will be ephemeral
|
116
|
+
#
|
117
|
+
# @option opts [bool] :sequence (false) if true, the created node will be sequential
|
118
|
+
#
|
119
|
+
# @option opts [ZookeeperCallbacks::StringCallback] :callback (nil) provide a callback object
|
120
|
+
# that will be called when the znode has been created
|
121
|
+
#
|
122
|
+
# @option opts [Object] :context (nil) an object passed to the +:callback+
|
123
|
+
# given as the +context+ param
|
124
|
+
#
|
125
|
+
# @option opts [:ephemeral_sequential, :persistent_sequential, :persistent, :ephemeral] :mode (nil)
|
126
|
+
# may be specified instead of :ephemeral and :sequence options. If +:mode+ *and* either of
|
127
|
+
# the +:ephermeral+ or +:sequential+ options are given, the +:mode+ option will win
|
128
|
+
#
|
129
|
+
# @raise [ZK::Exceptions::NodeExists] if a node with the same +path+ already exists
|
130
|
+
#
|
131
|
+
# @raise [ZK::Exceptions::NoNode] if the parent node does not exist
|
132
|
+
#
|
133
|
+
# @raise [ZK::Exceptions::NoChildrenForEphemerals] if the parent node of
|
134
|
+
# the given path is ephemeral
|
135
|
+
#
|
136
|
+
# @return [String] the path created on the server
|
137
|
+
#
|
138
|
+
# @todo Document the asynchronous methods
|
139
|
+
#
|
140
|
+
# @example create node, no data, persistent
|
141
|
+
#
|
142
|
+
# zk.create("/path")
|
143
|
+
# # => "/path"
|
144
|
+
#
|
145
|
+
# @example create node, ACL will default to ACL::OPEN_ACL_UNSAFE
|
146
|
+
#
|
147
|
+
# zk.create("/path", "foo")
|
148
|
+
# # => "/path"
|
149
|
+
#
|
150
|
+
# @example create ephemeral node
|
151
|
+
#
|
152
|
+
# zk.create("/path", '', :mode => :ephemeral)
|
153
|
+
# # => "/path"
|
154
|
+
#
|
155
|
+
# @example create sequential node
|
156
|
+
#
|
157
|
+
# zk.create("/path", '', :sequential => true)
|
158
|
+
# # => "/path0"
|
159
|
+
#
|
160
|
+
# # or you can also do:
|
161
|
+
#
|
162
|
+
# zk.create("/path", '', :mode => :persistent_sequence)
|
163
|
+
# # => "/path0"
|
164
|
+
#
|
165
|
+
#
|
166
|
+
# @example create ephemeral and sequential node
|
167
|
+
#
|
168
|
+
# zk.create("/path", '', :sequential => true, :ephemeral => true)
|
169
|
+
# # => "/path0"
|
170
|
+
#
|
171
|
+
# # or you can also do:
|
172
|
+
#
|
173
|
+
# zk.create("/path", "foo", :mode => :ephemeral_sequence)
|
174
|
+
# # => "/path0"
|
175
|
+
#
|
176
|
+
# @example create a child path
|
177
|
+
#
|
178
|
+
# zk.create("/path/child", "bar")
|
179
|
+
# # => "/path/child"
|
180
|
+
#
|
181
|
+
# @example create a sequential child path
|
182
|
+
#
|
183
|
+
# zk.create("/path/child", "bar", :sequential => true, :ephemeral => true)
|
184
|
+
# # => "/path/child0"
|
185
|
+
#
|
186
|
+
# # or you can also do:
|
187
|
+
#
|
188
|
+
# zk.create("/path/child", "bar", :mode => :ephemeral_sequence)
|
189
|
+
# # => "/path/child0"
|
190
|
+
#
|
191
|
+
# @hidden_example create asynchronously with callback object
|
192
|
+
#
|
193
|
+
# class StringCallback
|
194
|
+
# def process_result(return_code, path, context, name)
|
195
|
+
# # do processing here
|
196
|
+
# end
|
197
|
+
# end
|
198
|
+
#
|
199
|
+
# callback = StringCallback.new
|
200
|
+
# context = Object.new
|
201
|
+
#
|
202
|
+
# zk.create("/path", "foo", :callback => callback, :context => context)
|
203
|
+
#
|
204
|
+
# @hidden_example create asynchronously with callback proc
|
205
|
+
#
|
206
|
+
# callback = proc do |return_code, path, context, name|
|
207
|
+
# # do processing here
|
208
|
+
# end
|
209
|
+
#
|
210
|
+
# context = Object.new
|
211
|
+
#
|
212
|
+
# zk.create("/path", "foo", :callback => callback, :context => context)
|
213
|
+
#
|
214
|
+
def create(path, data='', opts={})
|
215
|
+
h = { :path => path, :data => data, :ephemeral => false, :sequence => false }.merge(opts)
|
216
|
+
|
217
|
+
if mode = h.delete(:mode)
|
218
|
+
mode = mode.to_sym
|
219
|
+
|
220
|
+
case mode
|
221
|
+
when :ephemeral_sequential
|
222
|
+
h[:ephemeral] = h[:sequence] = true
|
223
|
+
when :persistent_sequential
|
224
|
+
h[:ephemeral] = false
|
225
|
+
h[:sequence] = true
|
226
|
+
when :persistent
|
227
|
+
h[:ephemeral] = false
|
228
|
+
when :ephemeral
|
229
|
+
h[:ephemeral] = true
|
230
|
+
else
|
231
|
+
raise ArgumentError, "Unknown mode: #{mode.inspect}"
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
rv = check_rc(@cnx.create(h), h)
|
236
|
+
|
237
|
+
h[:callback] ? rv : rv[:path]
|
238
|
+
end
|
239
|
+
|
240
|
+
# Return the data and stat of the node of the given path.
|
241
|
+
#
|
242
|
+
# If +:watch+ is true and the call is successful (no exception is
|
243
|
+
# raised), registered watchers on the node will be 'armed'. The watch
|
244
|
+
# will be triggered by a successful operation that sets data on the node,
|
245
|
+
# or deletes the node. See +watcher+ for documentation on how to register
|
246
|
+
# blocks to be called when a watch event is fired.
|
247
|
+
#
|
248
|
+
# @todo fix references to Watcher documentation
|
249
|
+
#
|
250
|
+
# Supports being executed asynchronousy by passing a callback object.
|
251
|
+
#
|
252
|
+
# @param [String] path absolute path of the znode
|
253
|
+
#
|
254
|
+
# @option opts [bool] :watch (false) set to true if you want your registered
|
255
|
+
# callbacks for this node to be called on change
|
256
|
+
#
|
257
|
+
# @option opts [ZookeeperCallbacks::DataCallback] :callback to make this call asynchronously
|
258
|
+
#
|
259
|
+
# @option opts [Object] :context an object passed to the +:callback+
|
260
|
+
# given as the +context+ param
|
261
|
+
#
|
262
|
+
# @return [Array] a two-element array of ['node data', #<ZookeeperStat::Stat>]
|
263
|
+
#
|
264
|
+
# @raise [ZK::Exceptions::NoNode] if no node with the given path exists.
|
265
|
+
#
|
266
|
+
# @example get data for path
|
267
|
+
#
|
268
|
+
# zk.get("/path")
|
269
|
+
# # => ['this is the data', #<ZookeeperStat::Stat>]
|
270
|
+
#
|
271
|
+
# @example get data and set watch on node
|
272
|
+
#
|
273
|
+
# zk.get("/path", :watch => true)
|
274
|
+
# # => ['this is the data', #<ZookeeperStat::Stat>]
|
275
|
+
#
|
276
|
+
# @hidden_example get data asynchronously
|
277
|
+
#
|
278
|
+
# class DataCallback
|
279
|
+
# def process_result(return_code, path, context, data, stat)
|
280
|
+
# # do processing here
|
281
|
+
# end
|
282
|
+
# end
|
283
|
+
#
|
284
|
+
# zk.get("/path") do |return_code, path, context, data, stat|
|
285
|
+
# # do processing here
|
286
|
+
# end
|
287
|
+
#
|
288
|
+
# callback = DataCallback.new
|
289
|
+
# context = Object.new
|
290
|
+
# zk.get("/path", :callback => callback, :context => context)
|
291
|
+
#
|
292
|
+
def get(path, opts={})
|
293
|
+
h = { :path => path }.merge(opts)
|
294
|
+
|
295
|
+
setup_watcher!(:data, h)
|
296
|
+
|
297
|
+
rv = check_rc(@cnx.get(h), h)
|
298
|
+
|
299
|
+
opts[:callback] ? rv : rv.values_at(:data, :stat)
|
300
|
+
end
|
301
|
+
|
302
|
+
# Set the data for the node of the given path if such a node exists and the
|
303
|
+
# given version matches the version of the node (if the given version is
|
304
|
+
# -1, it matches any node's versions). Passing the version allows you to
|
305
|
+
# perform optimistic locking, in that if someone changes the node's
|
306
|
+
# data "behind your back", your update will fail. Since #create does not
|
307
|
+
# return a ZookeeperStat::Stat object, you should be aware that nodes are
|
308
|
+
# created with version == 0.
|
309
|
+
#
|
310
|
+
# This operation, if successful, will trigger all the watches on the node
|
311
|
+
# of the given path left by get calls.
|
312
|
+
#
|
313
|
+
# @raise [ZK::Exceptions::NoNode] raised if no node with the given path exists
|
314
|
+
#
|
315
|
+
# @raise [ZK::Exceptions::BadVersion] raised if the given version does not
|
316
|
+
# match the node's version
|
317
|
+
#
|
318
|
+
# Called with a hash of arguments set. Supports being executed
|
319
|
+
# asynchronousy by passing a callback object.
|
320
|
+
#
|
321
|
+
# @param [String] path absolute path of the znode
|
322
|
+
#
|
323
|
+
# @param [String] data the data to be set on the znode. Note that setting
|
324
|
+
# the data to the exact same value currently on the node still increments
|
325
|
+
# the node's version and causes watches to be fired.
|
326
|
+
#
|
327
|
+
# @option opts [Integer] :version (-1) matches all versions of a node if the
|
328
|
+
# default is used, otherwise acts as an assertion that the znode has the
|
329
|
+
# supplied version.
|
330
|
+
#
|
331
|
+
# @option opts [ZookeeperCallbacks::StatCallback] :callback will recieve the
|
332
|
+
# ZookeeperStat::Stat object asynchronously
|
333
|
+
#
|
334
|
+
# @option opts [Object] :context an object passed to the +:callback+
|
335
|
+
# given as the +context+ param
|
336
|
+
#
|
337
|
+
# @example unconditionally set the data of "/path"
|
338
|
+
#
|
339
|
+
# zk.set("/path", "foo")
|
340
|
+
#
|
341
|
+
# @example set the data of "/path" only if the version is 0
|
342
|
+
#
|
343
|
+
# zk.set("/path", "foo", :version => 0)
|
344
|
+
#
|
345
|
+
def set(path, data, opts={})
|
346
|
+
# ===== set data asynchronously
|
347
|
+
#
|
348
|
+
# class StatCallback
|
349
|
+
# def process_result(return_code, path, context, stat)
|
350
|
+
# # do processing here
|
351
|
+
# end
|
352
|
+
# end
|
353
|
+
#
|
354
|
+
# callback = StatCallback.new
|
355
|
+
# context = Object.new
|
356
|
+
#
|
357
|
+
# zk.set("/path", "foo", :callback => callback, :context => context)
|
358
|
+
|
359
|
+
h = { :path => path, :data => data }.merge(opts)
|
360
|
+
|
361
|
+
rv = check_rc(@cnx.set(h), h)
|
362
|
+
|
363
|
+
opts[:callback] ? nil : rv[:stat]
|
364
|
+
end
|
365
|
+
|
366
|
+
# Return the stat of the node of the given path. Return nil if the node
|
367
|
+
# doesn't exist.
|
368
|
+
#
|
369
|
+
# If the watch is true and the call is successful (no exception is thrown),
|
370
|
+
# a watch will be left on the node with the given path. The watch will be
|
371
|
+
# triggered by a successful operation that creates/delete the node or sets
|
372
|
+
# the data on the node.
|
373
|
+
#
|
374
|
+
# Can be called with just the path, otherwise a hash with the arguments
|
375
|
+
# set. Supports being executed asynchronousy by passing a callback object.
|
376
|
+
#
|
377
|
+
# @param [String] path absolute path of the znode
|
378
|
+
#
|
379
|
+
# @option opts [bool] :watch (false) set to true if you want to enable
|
380
|
+
# registered watches on this node
|
381
|
+
#
|
382
|
+
# @option opts [ZookeeperCallbacks::StatCallback] :callback will recieve the
|
383
|
+
# ZookeeperStat::Stat object asynchronously
|
384
|
+
#
|
385
|
+
# @option opts [Object] :context an object passed to the +:callback+
|
386
|
+
# given as the +context+ param
|
387
|
+
#
|
388
|
+
# @return [ZookeeperStat::Stat] a stat object of the specified node
|
389
|
+
#
|
390
|
+
# @example get stat for for path
|
391
|
+
# >> zk.stat("/path")
|
392
|
+
# # => ZK::Stat
|
393
|
+
#
|
394
|
+
# @example get stat for path and enable watchers
|
395
|
+
# >> zk.stat("/path", :watch => true)
|
396
|
+
# # => ZK::Stat
|
397
|
+
#
|
398
|
+
# @example exists for non existent path
|
399
|
+
#
|
400
|
+
# >> stat = zk.stat("/non_existent_path")
|
401
|
+
# # => #<ZookeeperStat::Stat:0x000001eb54 @exists=false>
|
402
|
+
# >> stat.exists?
|
403
|
+
# # => false
|
404
|
+
#
|
405
|
+
#
|
406
|
+
def stat(path, opts={})
|
407
|
+
# ===== exist node asynchronously
|
408
|
+
#
|
409
|
+
# class StatCallback
|
410
|
+
# def process_result(return_code, path, context, stat)
|
411
|
+
# # do processing here
|
412
|
+
# end
|
413
|
+
# end
|
414
|
+
#
|
415
|
+
# callback = StatCallback.new
|
416
|
+
# context = Object.new
|
417
|
+
#
|
418
|
+
# zk.exists?("/path", :callback => callback, :context => context)
|
419
|
+
|
420
|
+
|
421
|
+
h = { :path => path }.merge(opts)
|
422
|
+
|
423
|
+
setup_watcher!(:data, h)
|
424
|
+
|
425
|
+
rv = @cnx.stat(h)
|
426
|
+
|
427
|
+
return rv if opts[:callback]
|
428
|
+
|
429
|
+
case rv[:rc]
|
430
|
+
when Zookeeper::ZOK, Zookeeper::ZNONODE
|
431
|
+
rv[:stat]
|
432
|
+
else
|
433
|
+
check_rc(rv, h) # throws the appropriate error
|
434
|
+
end
|
435
|
+
end
|
436
|
+
|
437
|
+
|
438
|
+
# sugar around stat
|
439
|
+
#
|
440
|
+
# @example
|
441
|
+
#
|
442
|
+
# # instead of:
|
443
|
+
#
|
444
|
+
# zk.stat('/path').exists?
|
445
|
+
# # => true
|
446
|
+
#
|
447
|
+
# # you can do:
|
448
|
+
#
|
449
|
+
# zk.exists?('/path')
|
450
|
+
# # => true
|
451
|
+
#
|
452
|
+
# only works for the synchronous version of stat. for async version,
|
453
|
+
# this method will act *exactly* like stat
|
454
|
+
#
|
455
|
+
def exists?(path, opts={})
|
456
|
+
rv = stat(path, opts)
|
457
|
+
opts[:callback] ? rv : rv.exists?
|
458
|
+
end
|
459
|
+
|
460
|
+
# Return the list of the children of the node of the given path.
|
461
|
+
#
|
462
|
+
# If the watch is true and the call is successful (no exception is thrown),
|
463
|
+
# registered watchers of the children of the node will be enabled. The
|
464
|
+
# watch will be triggered by a successful operation that deletes the node
|
465
|
+
# of the given path or creates/delete a child under the node. See +watcher+
|
466
|
+
# for documentation on how to register blocks to be called when a watch
|
467
|
+
# event is fired.
|
468
|
+
#
|
469
|
+
# @raise [ZK::Exceptions::NoNode] if the node does not exist
|
470
|
+
#
|
471
|
+
# @param [String] path absolute path of the znode
|
472
|
+
#
|
473
|
+
# @option opts [bool] :watch (false) set to true if you want your registered
|
474
|
+
# callbacks for this node to be called on change
|
475
|
+
#
|
476
|
+
# @option opts [ZookeeperCallbacks::StringsCallback] :callback to make this
|
477
|
+
# call asynchronously
|
478
|
+
#
|
479
|
+
# @option opts [Object] :context an object passed to the +:callback+
|
480
|
+
# given as the +context+ param
|
481
|
+
#
|
482
|
+
# @example get children for path
|
483
|
+
#
|
484
|
+
# zk.create("/path", :data => "foo")
|
485
|
+
# zk.create("/path/child_0", :data => "child0")
|
486
|
+
# zk.create("/path/child_1", :data => "child1")
|
487
|
+
# zk.children("/path")
|
488
|
+
# # => ["child_0", "child_1"]
|
489
|
+
#
|
490
|
+
# @example get children and set watch
|
491
|
+
#
|
492
|
+
# # same setup as above
|
493
|
+
#
|
494
|
+
# zk.children("/path", :watch => true)
|
495
|
+
# # => ["child_0", "child_1"]
|
496
|
+
#
|
497
|
+
def children(path, opts={})
|
498
|
+
# ===== get children asynchronously
|
499
|
+
#
|
500
|
+
# class ChildrenCallback
|
501
|
+
# def process_result(return_code, path, context, children)
|
502
|
+
# # do processing here
|
503
|
+
# end
|
504
|
+
# end
|
505
|
+
#
|
506
|
+
# callback = ChildrenCallback.new
|
507
|
+
# context = Object.new
|
508
|
+
# zk.children("/path", :callback => callback, :context => context)
|
509
|
+
|
510
|
+
|
511
|
+
h = { :path => path }.merge(opts)
|
512
|
+
|
513
|
+
setup_watcher!(:child, h)
|
514
|
+
|
515
|
+
rv = check_rc(@cnx.get_children(h), h)
|
516
|
+
opts[:callback] ? nil : rv[:children]
|
517
|
+
end
|
518
|
+
|
519
|
+
# Delete the node with the given path. The call will succeed if such a node
|
520
|
+
# exists, and the given version matches the node's version (if the given
|
521
|
+
# version is -1, it matches any node's versions), and the node has no children.
|
522
|
+
#
|
523
|
+
# This operation, if successful, will trigger all the watches on the node
|
524
|
+
# of the given path left by exists API calls, and the watches on the parent
|
525
|
+
# node left by children API calls.
|
526
|
+
#
|
527
|
+
# Can be called with just the path, otherwise a hash with the arguments
|
528
|
+
# set. Supports being executed asynchronousy by passing a callback object.
|
529
|
+
#
|
530
|
+
# A KeeperException with error code KeeperException::NotEmpty will be
|
531
|
+
# thrown if the node has children.
|
532
|
+
#
|
533
|
+
# @raise [ZK::Exceptions::NoNode] raised if no node with the given path exists
|
534
|
+
#
|
535
|
+
# @raise [ZK::Exceptions::BadVersion] raised if the given version does not
|
536
|
+
# match the node's version
|
537
|
+
#
|
538
|
+
# @raise [ZK::Exceptions::NotEmpty] raised if the node has children
|
539
|
+
#
|
540
|
+
# @param [String] path absolute path of the znode
|
541
|
+
#
|
542
|
+
# @option opts [Integer] :version (-1) matches all versions of a node if the
|
543
|
+
# default is used, otherwise acts as an assertion that the znode has the
|
544
|
+
# supplied version.
|
545
|
+
#
|
546
|
+
# @option opts [ZookeeperCallbacks::VoidCallback] :callback will be called
|
547
|
+
# asynchronously when the operation is complete
|
548
|
+
#
|
549
|
+
# @option opts [Object] :context an object passed to the +:callback+
|
550
|
+
# given as the +context+ param
|
551
|
+
#
|
552
|
+
# @example delete a node
|
553
|
+
# zk.delete("/path")
|
554
|
+
#
|
555
|
+
# @example delete a node with a specific version
|
556
|
+
# zk.delete("/path", :version => 5)
|
557
|
+
#
|
558
|
+
def delete(path, opts={})
|
559
|
+
# ===== delete node asynchronously
|
560
|
+
#
|
561
|
+
# class VoidCallback
|
562
|
+
# def process_result(return_code, path, context)
|
563
|
+
# # do processing here
|
564
|
+
# end
|
565
|
+
# end
|
566
|
+
#
|
567
|
+
# callback = VoidCallback.new
|
568
|
+
# context = Object.new
|
569
|
+
#
|
570
|
+
# zk.delete(/path", :callback => callback, :context => context)
|
571
|
+
|
572
|
+
|
573
|
+
h = { :path => path, :version => -1 }.merge(opts)
|
574
|
+
rv = check_rc(@cnx.delete(h), h)
|
575
|
+
nil
|
576
|
+
end
|
577
|
+
|
578
|
+
# Return the ACL and stat of the node of the given path.
|
579
|
+
#
|
580
|
+
# @todo this method is pretty much untested, YMMV
|
581
|
+
#
|
582
|
+
# @raise [ZK::Exceptions::NoNode] if the parent node does not exist
|
583
|
+
#
|
584
|
+
# @param [String] path absolute path of the znode
|
585
|
+
#
|
586
|
+
# @option opts [ZookeeperStat::Stat] (nil) provide a Stat object that will
|
587
|
+
# be set with the Stat information of the node path
|
588
|
+
#
|
589
|
+
# @option opts [ZookeeperCallback::AclCallback] (nil) :callback for an
|
590
|
+
# asynchronous call to occur
|
591
|
+
#
|
592
|
+
# @option opts [Object] :context (nil) an object passed to the +:callback+
|
593
|
+
# given as the +context+ param
|
594
|
+
#
|
595
|
+
# @example get acl
|
596
|
+
#
|
597
|
+
# zk.get_acl("/path")
|
598
|
+
# # => [ACL]
|
599
|
+
#
|
600
|
+
# @example get acl with stat
|
601
|
+
#
|
602
|
+
# stat = ZK::Stat.new
|
603
|
+
# zk.get_acl("/path", :stat => stat)
|
604
|
+
# # => [ACL]
|
605
|
+
#
|
606
|
+
def get_acl(path, opts={})
|
607
|
+
# ===== get acl asynchronously
|
608
|
+
#
|
609
|
+
# class AclCallback
|
610
|
+
# def processResult(return_code, path, context, acl, stat)
|
611
|
+
# # do processing here
|
612
|
+
# end
|
613
|
+
# end
|
614
|
+
#
|
615
|
+
# callback = AclCallback.new
|
616
|
+
# context = Object.new
|
617
|
+
# zk.acls("/path", :callback => callback, :context => context)
|
618
|
+
|
619
|
+
h = { :path => path }.merge(opts)
|
620
|
+
rv = check_rc(@cnx.get_acl(h), h)
|
621
|
+
opts[:callback] ? nil : rv.values_at(:children, :stat)
|
622
|
+
end
|
623
|
+
|
624
|
+
# Set the ACL for the node of the given path if such a node exists and the
|
625
|
+
# given version matches the version of the node. Return the stat of the
|
626
|
+
# node.
|
627
|
+
#
|
628
|
+
# @raise [ZK::Exceptions::NoNode] if the parent node does not exist
|
629
|
+
#
|
630
|
+
# @raise [ZK::Exceptions::BadVersion] raised if the given version does not
|
631
|
+
# match the node's version
|
632
|
+
#
|
633
|
+
# @param [String] path absolute path of the znode
|
634
|
+
#
|
635
|
+
# @param [ZookeeperACLs] acls the acls to set on the znode
|
636
|
+
#
|
637
|
+
# @option opts [Integer] :version (-1) matches all versions of a node if the
|
638
|
+
# default is used, otherwise acts as an assertion that the znode has the
|
639
|
+
# supplied version.
|
640
|
+
#
|
641
|
+
# @option opts [ZookeeperCallbacks::VoidCallback] :callback will be called
|
642
|
+
# asynchronously when the operation is complete
|
643
|
+
#
|
644
|
+
# @option opts [Object] :context an object passed to the +:callback+
|
645
|
+
# given as the +context+ param
|
646
|
+
#
|
647
|
+
# @todo: TBA - waiting on clarification of method use
|
648
|
+
#
|
649
|
+
def set_acl(path, acls, opts={})
|
650
|
+
h = { :path => path, :acl => acls }.merge(opts)
|
651
|
+
rv = check_rc(@cnx.set_acl(h), h)
|
652
|
+
opts[:callback] ? nil : rv[:stat]
|
653
|
+
end
|
654
|
+
|
655
|
+
# @private
|
656
|
+
# @todo need to document this a little more
|
657
|
+
def set_debug_level(level)
|
658
|
+
if defined?(::JRUBY_VERSION)
|
659
|
+
warn "set_debug_level is not implemented for JRuby"
|
660
|
+
return
|
661
|
+
else
|
662
|
+
num =
|
663
|
+
case level
|
664
|
+
when String, Symbol
|
665
|
+
ZookeeperBase.const_get(:"ZOO_LOG_LEVEL_#{level.to_s.upcase}") rescue NameError
|
666
|
+
when Integer
|
667
|
+
level
|
668
|
+
end
|
669
|
+
|
670
|
+
raise ArgumentError, "#{level.inspect} is not a valid argument to set_debug_level" unless num
|
671
|
+
|
672
|
+
@cnx.set_debug_level(num)
|
673
|
+
end
|
674
|
+
end
|
675
|
+
|
676
|
+
protected
|
677
|
+
def check_rc(hash, inputs=nil)
|
678
|
+
hash.tap do |h|
|
679
|
+
if code = h[:rc]
|
680
|
+
msg = inputs ? "inputs: #{inputs.inspect}" : nil
|
681
|
+
raise Exceptions::KeeperException.by_code(code), msg unless code == Zookeeper::ZOK
|
682
|
+
end
|
683
|
+
end
|
684
|
+
end
|
685
|
+
|
686
|
+
def setup_watcher!(watch_type, opts)
|
687
|
+
event_handler.setup_watcher!(watch_type, opts)
|
688
|
+
end
|
689
|
+
end # Base
|
690
|
+
end # Client
|
691
|
+
end # ZK
|
692
|
+
|