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.
@@ -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
+