zk 0.6.5 → 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+