zookeeper 0.4.4 → 0.9.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. data/.gitignore +10 -0
  2. data/CHANGELOG +95 -0
  3. data/Gemfile +17 -0
  4. data/Manifest +8 -2
  5. data/README.markdown +59 -0
  6. data/Rakefile +137 -7
  7. data/ext/.gitignore +6 -0
  8. data/ext/Rakefile +51 -0
  9. data/ext/c_zookeeper.rb +212 -0
  10. data/ext/dbg.h +53 -0
  11. data/ext/depend +5 -0
  12. data/ext/extconf.rb +44 -15
  13. data/ext/generate_gvl_code.rb +316 -0
  14. data/ext/zkc-3.3.5.tar.gz +0 -0
  15. data/ext/zkrb_wrapper.c +731 -0
  16. data/ext/zkrb_wrapper.h +330 -0
  17. data/ext/zkrb_wrapper_compat.c +15 -0
  18. data/ext/zkrb_wrapper_compat.h +11 -0
  19. data/ext/zookeeper_base.rb +211 -0
  20. data/ext/zookeeper_c.c +268 -97
  21. data/ext/zookeeper_lib.c +157 -92
  22. data/ext/zookeeper_lib.h +12 -6
  23. data/java/zookeeper_base.rb +477 -0
  24. data/lib/zookeeper/acls.rb +10 -1
  25. data/lib/zookeeper/callbacks.rb +5 -3
  26. data/lib/zookeeper/common/queue_with_pipe.rb +78 -0
  27. data/lib/zookeeper/common.rb +174 -0
  28. data/lib/zookeeper/constants.rb +31 -28
  29. data/lib/zookeeper/em_client.rb +55 -0
  30. data/lib/zookeeper/exceptions.rb +10 -2
  31. data/lib/zookeeper/stat.rb +11 -2
  32. data/lib/zookeeper/version.rb +6 -0
  33. data/lib/zookeeper.rb +155 -122
  34. data/notes.txt +14 -0
  35. data/spec/c_zookeeper_spec.rb +50 -0
  36. data/spec/chrooted_connection_spec.rb +81 -0
  37. data/spec/default_watcher_spec.rb +41 -0
  38. data/spec/em_spec.rb +51 -0
  39. data/spec/log4j.properties +17 -0
  40. data/spec/shared/all_success_return_values.rb +10 -0
  41. data/spec/shared/connection_examples.rb +1018 -0
  42. data/spec/spec_helper.rb +119 -0
  43. data/spec/support/progress_formatter.rb +15 -0
  44. data/spec/zookeeper_spec.rb +24 -0
  45. data/zookeeper.gemspec +37 -25
  46. metadata +78 -34
  47. data/README +0 -42
  48. data/ext/zkc-3.3.2.tar.gz +0 -0
data/lib/zookeeper.rb CHANGED
@@ -1,59 +1,54 @@
1
1
  # Ruby wrapper for the Zookeeper C API
2
2
 
3
- require 'zookeeper_c'
4
3
  require 'thread'
5
- require 'zookeeper/callbacks'
4
+ require 'monitor'
5
+ require 'forwardable'
6
+ require 'logger'
7
+
8
+ require 'zookeeper/common'
6
9
  require 'zookeeper/constants'
10
+ require 'zookeeper/callbacks'
7
11
  require 'zookeeper/exceptions'
8
12
  require 'zookeeper/stat'
9
13
  require 'zookeeper/acls'
10
14
 
11
- class Zookeeper < CZookeeper
12
- include ZookeeperCallbacks
13
- include ZookeeperConstants
14
- include ZookeeperExceptions
15
- include ZookeeperACLs
16
- include ZookeeperStat
17
-
18
- ZKRB_GLOBAL_CB_REQ = -1
19
-
20
- # debug levels
21
- ZOO_LOG_LEVEL_ERROR = 1
22
- ZOO_LOG_LEVEL_WARN = 2
23
- ZOO_LOG_LEVEL_INFO = 3
24
- ZOO_LOG_LEVEL_DEBUG = 4
25
-
26
- def reopen(timeout = 10)
27
- init(@host)
28
- if timeout > 0
29
- time_to_stop = Time.now + timeout
30
- until state == Zookeeper::ZOO_CONNECTED_STATE
31
- break if Time.now > time_to_stop
32
- sleep 0.1
33
- end
34
- end
35
- # flushes all outstanding watcher reqs.
36
- @watcher_reqs = { ZKRB_GLOBAL_CB_REQ => { :watcher => get_default_global_watcher } }
37
- setup_dispatch_thread!
38
- state
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
39
31
  end
40
32
 
41
- def initialize(host, timeout = 10)
42
- @watcher_reqs = {}
43
- @completion_reqs = {}
44
- @req_mutex = Mutex.new
45
- @current_req_id = 1
46
- @host = host
47
- return nil if reopen(timeout) != Zookeeper::ZOO_CONNECTED_STATE
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
48
44
  end
49
45
 
50
- public
51
46
  def get(options = {})
52
47
  assert_open
53
48
  assert_supported_keys(options, [:path, :watcher, :watcher_context, :callback, :callback_context])
54
49
  assert_required_keys(options, [:path])
55
50
 
56
- req_id = setup_call(options)
51
+ req_id = setup_call(:get, options)
57
52
  rc, value, stat = super(req_id, options[:path], options[:callback], options[:watcher])
58
53
 
59
54
  rv = { :req_id => req_id, :rc => rc }
@@ -64,9 +59,10 @@ public
64
59
  assert_open
65
60
  assert_supported_keys(options, [:path, :data, :version, :callback, :callback_context])
66
61
  assert_required_keys(options, [:path])
62
+ assert_valid_data_size!(options[:data])
67
63
  options[:version] ||= -1
68
64
 
69
- req_id = setup_call(options)
65
+ req_id = setup_call(:set, options)
70
66
  rc, stat = super(req_id, options[:path], options[:data], options[:callback], options[:version])
71
67
 
72
68
  rv = { :req_id => req_id, :rc => rc }
@@ -78,7 +74,7 @@ public
78
74
  assert_supported_keys(options, [:path, :callback, :callback_context, :watcher, :watcher_context])
79
75
  assert_required_keys(options, [:path])
80
76
 
81
- req_id = setup_call(options)
77
+ req_id = setup_call(:get_children, options)
82
78
  rc, children, stat = super(req_id, options[:path], options[:callback], options[:watcher])
83
79
 
84
80
  rv = { :req_id => req_id, :rc => rc }
@@ -90,7 +86,7 @@ public
90
86
  assert_supported_keys(options, [:path, :callback, :callback_context, :watcher, :watcher_context])
91
87
  assert_required_keys(options, [:path])
92
88
 
93
- req_id = setup_call(options)
89
+ req_id = setup_call(:stat, options)
94
90
  rc, stat = exists(req_id, options[:path], options[:callback], options[:watcher])
95
91
 
96
92
  rv = { :req_id => req_id, :rc => rc }
@@ -101,6 +97,7 @@ public
101
97
  assert_open
102
98
  assert_supported_keys(options, [:path, :data, :acl, :ephemeral, :sequence, :callback, :callback_context])
103
99
  assert_required_keys(options, [:path])
100
+ assert_valid_data_size!(options[:data])
104
101
 
105
102
  flags = 0
106
103
  flags |= ZOO_EPHEMERAL if options[:ephemeral]
@@ -108,7 +105,7 @@ public
108
105
 
109
106
  options[:acl] ||= ZOO_OPEN_ACL_UNSAFE
110
107
 
111
- req_id = setup_call(options)
108
+ req_id = setup_call(:create, options)
112
109
  rc, newpath = super(req_id, options[:path], options[:data], options[:callback], options[:acl], flags)
113
110
 
114
111
  rv = { :req_id => req_id, :rc => rc }
@@ -121,19 +118,37 @@ public
121
118
  assert_required_keys(options, [:path])
122
119
  options[:version] ||= -1
123
120
 
124
- req_id = setup_call(options)
121
+ req_id = setup_call(:delete, options)
125
122
  rc = super(req_id, options[:path], options[:version], options[:callback])
126
123
 
127
124
  { :req_id => req_id, :rc => rc }
128
125
  end
129
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
+
130
145
  def set_acl(options = {})
131
146
  assert_open
132
147
  assert_supported_keys(options, [:path, :acl, :version, :callback, :callback_context])
133
148
  assert_required_keys(options, [:path, :acl])
134
149
  options[:version] ||= -1
135
150
 
136
- req_id = setup_call(options)
151
+ req_id = setup_call(:set_acl, options)
137
152
  rc = super(req_id, options[:path], options[:acl], options[:callback], options[:version])
138
153
 
139
154
  { :req_id => req_id, :rc => rc }
@@ -144,121 +159,139 @@ public
144
159
  assert_supported_keys(options, [:path, :callback, :callback_context])
145
160
  assert_required_keys(options, [:path])
146
161
 
147
- req_id = setup_call(options)
162
+ req_id = setup_call(:get_acl, options)
148
163
  rc, acls, stat = super(req_id, options[:path], options[:callback])
149
164
 
150
165
  rv = { :req_id => req_id, :rc => rc }
151
166
  options[:callback] ? rv : rv.merge(:acl => acls, :stat => Stat.new(stat))
152
167
  end
153
168
 
154
- # To close a Zk handle, first shutdown the dispatcher thread; this is done by
155
- # signalling the waiting thread that there is a pending close. We then release
156
- # the C-land Zk state.
169
+ # close this client and any underyling connections
157
170
  def close
158
- signal_pending_close
159
- @dispatcher.join
160
171
  super
161
172
  end
162
173
 
163
- private
164
- def setup_dispatch_thread!
165
- @dispatcher = Thread.new {
166
- while true do
167
- hash = get_next_event
168
- break if hash.nil? # Pending close => exit dispatcher thread
169
- dispatch_event(hash)
170
- end
171
- }
174
+ def state
175
+ super
172
176
  end
173
177
 
174
- def dispatch_event(hash)
175
- is_completion = hash.has_key?(:rc)
178
+ def connected?
179
+ super
180
+ end
176
181
 
177
- hash[:stat] = Stat.new(hash[:stat]) if hash.has_key?(:stat)
178
- hash[:acl] = hash[:acl].map { |acl| ACL.new(acl) } if hash[:acl]
182
+ def connecting?
183
+ super
184
+ end
179
185
 
180
- callback_context = is_completion ? get_completion(hash[:req_id]) : get_watcher(hash[:req_id])
181
- callback = is_completion ? callback_context[:callback] : callback_context[:watcher]
182
- hash[:context] = callback_context[:context]
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
183
199
 
184
- # TODO: Eventually enforce derivation from Zookeeper::Callback
185
- if callback.respond_to?(:call)
186
- callback.call(hash)
187
- else
188
- # puts "dispatch_next_callback found non-callback => #{callback.inspect}"
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)
189
207
  end
190
208
  end
191
209
 
192
- def setup_call(opts)
193
- req_id = nil
194
- @req_mutex.synchronize {
195
- req_id = @current_req_id
196
- @current_req_id += 1
197
- setup_completion(req_id, opts) if opts[:callback]
198
- setup_watcher(req_id, opts) if opts[:watcher]
199
- }
200
- req_id
210
+ # @private
211
+ def self.get_debug_level
212
+ if defined?(::CZookeeper)
213
+ CZookeeper.get_debug_level
214
+ end
201
215
  end
202
216
 
203
- def setup_watcher(req_id, call_opts)
204
- @watcher_reqs[req_id] = { :watcher => call_opts[:watcher],
205
- :context => call_opts[:watcher_context] }
217
+ class << self
218
+ # @private
219
+ alias :debug_level= :set_debug_level
220
+
221
+ # @private
222
+ alias :debug_level :get_debug_level
206
223
  end
207
224
 
208
- def setup_completion(req_id, call_opts)
209
- @completion_reqs[req_id] = { :callback => call_opts[:callback],
210
- :context => call_opts[:callback_context] }
225
+ # DEPRECATED: use the class-level method instead
226
+ def set_debug_level(val)
227
+ super
211
228
  end
212
229
 
213
- def get_watcher(req_id)
214
- @req_mutex.synchronize {
215
- req_id != ZKRB_GLOBAL_CB_REQ ? @watcher_reqs.delete(req_id) : @watcher_reqs[req_id]
216
- }
230
+ # has the underlying connection been closed?
231
+ def closed?
232
+ super
217
233
  end
218
234
 
219
- def get_completion(req_id)
220
- @req_mutex.synchronize { @completion_reqs.delete(req_id) }
235
+ # is the event delivery system running?
236
+ def running?
237
+ super
221
238
  end
222
239
 
223
- public
224
- # TODO: Sanitize user mistakes by unregistering watchers from ops that
225
- # don't return ZOK (except wexists)? Make users clean up after themselves for now.
226
- def unregister_watcher(req_id)
227
- @req_mutex.synchronize {
228
- @watcher_reqs.delete(req_id)
229
- }
240
+ # return the session id of the current connection as an Fixnum
241
+ def session_id
242
+ super
230
243
  end
231
244
 
232
- private
233
- # TODO: Make all global puts configurable
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
234
263
  def get_default_global_watcher
235
- Proc.new { |args|
236
- # puts "Ruby ZK Global CB called type=#{event_by_value(args[:type])} state=#{state_by_value(args[:state])}"
237
- true
238
- }
264
+ super
239
265
  end
240
266
 
241
- # if either of these happen, the user will need to renegotiate a connection via reopen
242
- def assert_open
243
- if state == ZOO_EXPIRED_SESSION_STATE
244
- raise ZookeeperException::SessionExpired
245
- elsif state != Zookeeper::ZOO_CONNECTED_STATE
246
- raise ZookeeperException::ConnectionClosed
247
- end
267
+ def logger
268
+ Zookeeper.logger
248
269
  end
249
270
 
250
- def assert_supported_keys(args, supported)
251
- unless (args.keys - supported).empty?
252
- raise ZookeeperException::BadArguments,
253
- "Supported arguments are: #{supported.inspect}, but arguments #{args.keys.inspect} were supplied instead"
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}"
254
277
  end
278
+ nil
255
279
  end
256
280
 
257
- def assert_required_keys(args, required)
258
- unless (required - args.keys).empty?
259
- raise ZookeeperException::BadArguments,
260
- "Required arguments are: #{required.inspect}, but only the arguments #{args.keys.inspect} were supplied."
261
- end
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
262
295
  end
263
296
  end
264
297
 
data/notes.txt ADDED
@@ -0,0 +1,14 @@
1
+ Notes on this fork of http://github.com/twitter/zookeeper
2
+
3
+ The main purpose of this fork is to provide JRuby compatibility with the MRI driver of the original. We have been using an early fork with an incomplete binding to the C client, and had recently bumped up against a fairly serious bug with handling events. The twitter/zookeeper driver solved this problem but was lacking JRuby support (which is a core piece of our environment).
4
+
5
+ I've packaged the Java client (and its dependency on log4j) as gems that are installed separately from this codebase to cut down on the size of the zookeeper gem. If this poses a problem (for instance, if the original goal was to have an all-in-one install with no external dependencies), it would be trivial to package those jars along with this code.
6
+
7
+ In the course of writing the wrapper for JRuby, I've written nearly-complete specs for all of the public API methods: get, set, get_children, stat, create, delete, get_acl, and set_acl. The reason I say they're "nearly" complete is that I have no use for set_acl, and quite honestly, couldn't figure out how to make it work.
8
+
9
+ I'm planning on writing a companion gem, 'ZK', that would take some of the higher-level constructs that we'd written around the lower-level driver to implement features like locking and queues, and also to provide a more ruby-like interface.
10
+
11
+ I'd like to reorganize this codebase in a number of ways, most of all to move all of the various classes and modules under a common "Zookeeper" namespace, however I thought that would be a radical enough change where I'd want to discuss/coordinate with you on how exactly to do it.
12
+
13
+
14
+
@@ -0,0 +1,50 @@
1
+ # tests the CZookeeper, obviously only available when running under MRI
2
+ require 'spec_helper'
3
+
4
+ if Module.const_defined?(:CZookeeper)
5
+ describe CZookeeper do
6
+ def pop_all_events
7
+ [].tap do |rv|
8
+ begin
9
+ rv << @event_queue.pop(non_blocking=true)
10
+ rescue ThreadError
11
+ end
12
+ end
13
+ end
14
+
15
+ def wait_until_connected(timeout=2)
16
+ wait_until(timeout) { @czk.state == ZookeeperConstants::ZOO_CONNECTED_STATE }
17
+ end
18
+
19
+ describe do
20
+ before do
21
+ @event_queue = ZookeeperCommon::QueueWithPipe.new
22
+ @czk = CZookeeper.new('localhost:2181', @event_queue)
23
+ end
24
+
25
+ after do
26
+ @czk.close rescue Exception
27
+ @event_queue.close rescue Exception
28
+ end
29
+
30
+ it %[should be in connected state within a reasonable amount of time] do
31
+ wait_until_connected.should be_true
32
+ end
33
+
34
+ describe :after_connected do
35
+ before do
36
+ wait_until_connected.should be_true
37
+ end
38
+
39
+ it %[should have a connection event after being connected] do
40
+ event = wait_until(2) { @event_queue.pop }
41
+ event.should be
42
+ event[:req_id].should == ZookeeperCommon::ZKRB_GLOBAL_CB_REQ
43
+ event[:type].should == ZookeeperConstants::ZOO_SESSION_EVENT
44
+ event[:state].should == ZookeeperConstants::ZOO_CONNECTED_STATE
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+
@@ -0,0 +1,81 @@
1
+ require 'spec_helper'
2
+ require 'shared/connection_examples'
3
+
4
+ describe 'Zookeeper chrooted' do
5
+ let(:path) { "/_zkchroottest_" }
6
+ let(:data) { "underpants" }
7
+ let(:chroot_path) { '/slyphon-zookeeper-chroot' }
8
+
9
+ let(:connection_string) { "localhost:2181#{chroot_path}" }
10
+
11
+ before do
12
+ @zk = Zookeeper.new(connection_string)
13
+ end
14
+
15
+ after do
16
+ @zk and @zk.close
17
+ end
18
+
19
+ def zk
20
+ @zk
21
+ end
22
+
23
+ describe 'non-existent' do
24
+ describe 'with existing parent' do
25
+ let(:chroot_path) { '/one-level' }
26
+
27
+ describe 'create' do
28
+ before do
29
+ with_open_zk do |z|
30
+ rm_rf(z, chroot_path)
31
+ end
32
+ end
33
+
34
+ it %[should successfully create the path] do
35
+ rv = zk.create(:path => '/', :data => '')
36
+ rv[:rc].should be_zero
37
+ rv[:path].should == ''
38
+ end
39
+ end
40
+ end
41
+
42
+ describe 'with missing parent' do
43
+ let(:chroot_path) { '/deeply/nested/path' }
44
+
45
+ describe 'create' do
46
+ before do
47
+ with_open_zk do |z|
48
+ rm_rf(z, chroot_path)
49
+ end
50
+ end
51
+
52
+ it %[should return ZNONODE] do
53
+ rv = zk.create(:path => '/', :data => '')
54
+ rv[:rc].should_not be_zero
55
+ rv[:rc].should == ZookeeperExceptions::ZNONODE
56
+ end
57
+ end
58
+ end
59
+ end
60
+
61
+
62
+ describe do
63
+ before :all do
64
+ Zookeeper.logger.warn "running before :all"
65
+
66
+ with_open_zk do |z|
67
+ z.create(:path => chroot_path, :data => '')
68
+ end
69
+ end
70
+
71
+ after :all do
72
+ with_open_zk do |z|
73
+ rm_rf(z, chroot_path)
74
+ end
75
+ end
76
+
77
+ it_should_behave_like "connection"
78
+ end
79
+ end
80
+
81
+
@@ -0,0 +1,41 @@
1
+ require File.expand_path('../spec_helper', __FILE__)
2
+
3
+ describe Zookeeper do
4
+ describe :initialize, 'with watcher block' do
5
+ before do
6
+ @events = []
7
+ @watch_block = lambda do |hash|
8
+ $stderr.puts "watch_block: #{hash.inspect}"
9
+ @events << hash
10
+ end
11
+
12
+ @zk = Zookeeper.new('localhost:2181', 10, @watch_block)
13
+
14
+ wait_until(2) { @zk.connected? }
15
+ @zk.should be_connected
16
+ $stderr.puts "connected!"
17
+
18
+ wait_until(2) { !@events.empty? }
19
+ $stderr.puts "got events!"
20
+ end
21
+
22
+ after do
23
+ @zk.close if @zk.connected?
24
+ end
25
+
26
+ it %[should receive initial connection state events] do
27
+ @events.should_not be_empty
28
+ @events.length.should == 1
29
+ @events.first[:state].should == Zookeeper::ZOO_CONNECTED_STATE
30
+ end
31
+
32
+ it %[should receive disconnection events] do
33
+ pending "the C driver doesn't appear to deliver disconnection events (?)"
34
+ @events.clear
35
+ @zk.close
36
+ wait_until(2) { !@events.empty? }
37
+ @events.should_not be_empty
38
+ end
39
+ end
40
+ end
41
+
data/spec/em_spec.rb ADDED
@@ -0,0 +1,51 @@
1
+ require File.expand_path('../spec_helper', __FILE__)
2
+ require 'zookeeper/em_client'
3
+
4
+ gem 'evented-spec', '~> 0.9.0'
5
+ require 'evented-spec'
6
+
7
+
8
+ describe 'ZookeeperEM' do
9
+ describe 'Client' do
10
+ include EventedSpec::SpecHelper
11
+ default_timeout 3.0
12
+
13
+ def setup_zk
14
+ @zk = ZookeeperEM::Client.new('localhost:2181')
15
+ em do
16
+ @zk.on_attached do
17
+ yield
18
+ end
19
+ end
20
+ end
21
+
22
+ def teardown_and_done
23
+ @zk.close do
24
+ logger.debug { "TEST: about to call done" }
25
+ EM.next_tick do
26
+ done
27
+ end
28
+ end
29
+ end
30
+
31
+ describe 'callbacks' do
32
+ it %[should be called on the reactor thread] do
33
+ cb = lambda do |h|
34
+ EM.reactor_thread?.should be_true
35
+ logger.debug { "called back on the reactor thread? #{EM.reactor_thread?}" }
36
+ teardown_and_done
37
+ end
38
+
39
+ setup_zk do
40
+ @zk.on_attached do |*|
41
+ logger.debug { "on_attached called" }
42
+ rv = @zk.get(:path => '/', :callback => cb)
43
+ logger.debug { "rv from @zk.get: #{rv.inspect}" }
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+
@@ -0,0 +1,17 @@
1
+ log4j.rootLogger = DEBUG,S
2
+
3
+ log4j.appender.S = org.apache.log4j.FileAppender
4
+ log4j.appender.S.File = /tmp/zk-test.log
5
+
6
+ log4j.appender.S.layout = org.apache.log4j.PatternLayout
7
+ log4j.appender.S.layout.ConversionPattern = %5p [%t] (%c:%F:%L) - %m%n
8
+
9
+ log4j.appender.C = org.apache.log4j.FileAppender
10
+ log4j.appender.C.File = /tmp/zk-test-client.log
11
+
12
+ log4j.appender.C.layout = org.apache.log4j.PatternLayout
13
+ log4j.appender.C.layout.ConversionPattern = %5p [%t] (%c:%F:%L) - %m%n
14
+
15
+ log4j.org.apache.zookeeper.ZooKeeper = DEBUG,C
16
+ log4j.org.apache.zookeeper.server = DEBUG,S
17
+
@@ -0,0 +1,10 @@
1
+ shared_examples_for "all success return values" do
2
+ it %[should have a return code of Zookeeper::ZOK] do
3
+ @rv[:rc].should == Zookeeper::ZOK
4
+ end
5
+
6
+ it %[should have a req_id integer] do
7
+ @rv[:req_id].should be_kind_of(Integer)
8
+ end
9
+ end
10
+