slyphon-zookeeper 0.1.0-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,165 @@
1
+ #ifndef ZOOKEEPER_LIB_H
2
+ #define ZOOKEEPER_LIB_H
3
+
4
+ #include "ruby.h"
5
+ #include "c-client-src/zookeeper.h"
6
+ #include <errno.h>
7
+ #include <stdio.h>
8
+ #include <stdlib.h>
9
+
10
+ #define ZK_TRUE 1
11
+ #define ZK_FALSE 0
12
+ #define ZKRB_GLOBAL_REQ -1
13
+
14
+ #ifndef RSTRING_LEN
15
+ # define RSTRING_LEN(x) RSTRING(x)->len
16
+ #endif
17
+ #ifndef RSTRING_PTR
18
+ # define RSTRING_PTR(x) RSTRING(x)->ptr
19
+ #endif
20
+ #ifndef RARRAY_LEN
21
+ # define RARRAY_LEN(x) RARRAY(x)->len
22
+ #endif
23
+
24
+ extern int ZKRBDebugging;
25
+ extern pthread_mutex_t zkrb_q_mutex;
26
+
27
+ struct zkrb_data_completion {
28
+ char *data;
29
+ int data_len;
30
+ struct Stat *stat;
31
+ };
32
+
33
+ struct zkrb_stat_completion {
34
+ struct Stat *stat;
35
+ };
36
+
37
+ struct zkrb_void_completion {
38
+ };
39
+
40
+ struct zkrb_string_completion {
41
+ char *value;
42
+ };
43
+
44
+ struct zkrb_strings_completion {
45
+ struct String_vector *values;
46
+ };
47
+
48
+ struct zkrb_strings_stat_completion {
49
+ struct String_vector *values;
50
+ struct Stat *stat;
51
+ };
52
+
53
+ struct zkrb_acl_completion {
54
+ struct ACL_vector *acl;
55
+ struct Stat *stat;
56
+ };
57
+
58
+ struct zkrb_watcher_completion {
59
+ int type;
60
+ int state;
61
+ char *path;
62
+ };
63
+
64
+ typedef struct {
65
+ int64_t req_id;
66
+ int rc;
67
+
68
+ enum {
69
+ ZKRB_DATA = 0,
70
+ ZKRB_STAT = 1,
71
+ ZKRB_VOID = 2,
72
+ ZKRB_STRING = 3,
73
+ ZKRB_STRINGS = 4,
74
+ ZKRB_STRINGS_STAT = 5,
75
+ ZKRB_ACL = 6,
76
+ ZKRB_WATCHER = 7
77
+ } type;
78
+
79
+ union {
80
+ struct zkrb_data_completion *data_completion;
81
+ struct zkrb_stat_completion *stat_completion;
82
+ struct zkrb_void_completion *void_completion;
83
+ struct zkrb_string_completion *string_completion;
84
+ struct zkrb_strings_completion *strings_completion;
85
+ struct zkrb_strings_stat_completion *strings_stat_completion;
86
+ struct zkrb_acl_completion *acl_completion;
87
+ struct zkrb_watcher_completion *watcher_completion;
88
+ } completion;
89
+ } zkrb_event_t;
90
+
91
+ struct zkrb_event_ll_t {
92
+ zkrb_event_t *event;
93
+ struct zkrb_event_ll_t *next;
94
+ };
95
+
96
+ typedef struct {
97
+ struct zkrb_event_ll_t *head;
98
+ struct zkrb_event_ll_t *tail;
99
+ int pipe_read;
100
+ int pipe_write;
101
+ } zkrb_queue_t;
102
+
103
+ zkrb_queue_t * zkrb_queue_alloc(void);
104
+ void zkrb_queue_free(zkrb_queue_t *queue);
105
+ zkrb_event_t * zkrb_event_alloc(void);
106
+ void zkrb_event_free(zkrb_event_t *ptr);
107
+
108
+ /* push/pop is a misnomer, this is a queue */
109
+ void zkrb_enqueue(zkrb_queue_t *queue, zkrb_event_t *elt);
110
+ zkrb_event_t * zkrb_peek(zkrb_queue_t *queue);
111
+ zkrb_event_t * zkrb_dequeue(zkrb_queue_t *queue, int need_lock);
112
+
113
+ void zkrb_print_stat(const struct Stat *s);
114
+
115
+ typedef struct {
116
+ int64_t req_id;
117
+ zkrb_queue_t *queue;
118
+ } zkrb_calling_context;
119
+
120
+ void zkrb_print_calling_context(zkrb_calling_context *ctx);
121
+ zkrb_calling_context *zkrb_calling_context_alloc(int64_t req_id, zkrb_queue_t *queue);
122
+
123
+ /*
124
+ default process completions that get queued into the ruby client event queue
125
+ */
126
+
127
+ void zkrb_state_callback(
128
+ zhandle_t *zh, int type, int state, const char *path, void *calling_ctx);
129
+
130
+ void zkrb_data_callback(
131
+ int rc, const char *value, int value_len, const struct Stat *stat, const void *calling_ctx);
132
+
133
+ void zkrb_stat_callback(
134
+ int rc, const struct Stat *stat, const void *calling_ctx);
135
+
136
+ void zkrb_string_callback(
137
+ int rc, const char *string, const void *calling_ctx);
138
+
139
+ void zkrb_strings_callback(
140
+ int rc, const struct String_vector *strings, const void *calling_ctx);
141
+
142
+ void zkrb_strings_stat_callback(
143
+ int rc, const struct String_vector *strings, const struct Stat* stat, const void *calling_ctx);
144
+
145
+ void zkrb_void_callback(
146
+ int rc, const void *calling_ctx);
147
+
148
+ void zkrb_acl_callback(
149
+ int rc, struct ACL_vector *acls, struct Stat *stat, const void *calling_ctx);
150
+
151
+ VALUE zkrb_event_to_ruby(zkrb_event_t *event);
152
+ VALUE zkrb_acl_to_ruby(struct ACL *acl);
153
+ VALUE zkrb_acl_vector_to_ruby(struct ACL_vector *acl_vector);
154
+ VALUE zkrb_id_to_ruby(struct Id *id);
155
+ VALUE zkrb_string_vector_to_ruby(struct String_vector *string_vector);
156
+ VALUE zkrb_stat_to_rarray(const struct Stat *stat);
157
+ VALUE zkrb_stat_to_rhash(const struct Stat* stat);
158
+
159
+ struct ACL_vector * zkrb_ruby_to_aclvector(VALUE acl_ary);
160
+ struct ACL_vector * zkrb_clone_acl_vector(struct ACL_vector * src);
161
+ struct String_vector * zkrb_clone_string_vector(const struct String_vector * src);
162
+ struct ACL zkrb_ruby_to_acl(VALUE rubyacl);
163
+ struct Id zkrb_ruby_to_id(VALUE rubyid);
164
+
165
+ #endif /* ZOOKEEPER_LIB_H */
@@ -0,0 +1,426 @@
1
+ require 'java'
2
+ require 'thread'
3
+ require 'rubygems'
4
+
5
+ gem 'slyphon-log4j', '= 1.2.15'
6
+ gem 'slyphon-zookeeper_jar', '= 3.3.1'
7
+
8
+ require 'log4j'
9
+ require 'zookeeper_jar'
10
+
11
+ # The low-level wrapper-specific methods for the Java lib,
12
+ # subclassed by the top-level Zookeeper class
13
+ class ZookeeperBase
14
+ include Java
15
+ include ZookeeperCommon
16
+ include ZookeeperConstants
17
+ include ZookeeperCallbacks
18
+ include ZookeeperExceptions
19
+ include ZookeeperACLs
20
+ include ZookeeperStat
21
+
22
+ JZK = org.apache.zookeeper
23
+ JZKD = org.apache.zookeeper.data
24
+ Code = JZK::KeeperException::Code
25
+
26
+ ANY_VERSION = -1
27
+ DEFAULT_SESSION_TIMEOUT = 10_000
28
+
29
+ ZKRB_GLOBAL_CB_REQ = -1 unless defined?(ZKRB_GLOBAL_CB_REQ)
30
+
31
+ JZKD::Stat.class_eval do
32
+ MEMBERS = [:version, :czxid, :mzxid, :ctime, :mtime, :cversion, :aversion, :ephemeralOwner, :dataLength, :numChildren, :pzxid]
33
+ def to_hash
34
+ MEMBERS.inject({}) { |h,k| h[k] = __send__(k); h }
35
+ end
36
+ end
37
+
38
+ JZKD::Id.class_eval do
39
+ def to_hash
40
+ { :scheme => getScheme, :id => getId }
41
+ end
42
+ end
43
+
44
+ JZKD::ACL.class_eval do
45
+ def self.from_ruby_acl(acl)
46
+ raise TypeError, "acl must be a ZookeeperACLs::ACL not #{acl.inspect}" unless acl.kind_of?(ZookeeperACLs::ACL)
47
+ id = org.apache.zookeeper.data.Id.new(acl.id.scheme.to_s, acl.id.id.to_s)
48
+ new(acl.perms.to_i, id)
49
+ end
50
+
51
+ def to_hash
52
+ { :perms => getPerms, :id => getId.to_hash }
53
+ end
54
+ end
55
+
56
+ JZK::WatchedEvent.class_eval do
57
+ def to_hash
58
+ { :type => getType.getIntValue, :state => getState.getIntValue, :path => getPath }
59
+ end
60
+ end
61
+
62
+ # used for internal dispatching
63
+ module JavaCB #:nodoc:
64
+ class Callback
65
+ attr_reader :req_id
66
+
67
+ def initialize(req_id)
68
+ @req_id = req_id
69
+ end
70
+
71
+ protected
72
+ def logger
73
+ Zookeeper.logger
74
+ end
75
+ end
76
+
77
+ class DataCallback < Callback
78
+ include JZK::AsyncCallback::DataCallback
79
+
80
+ def processResult(rc, path, queue, data, stat)
81
+ queue.push({
82
+ :rc => rc,
83
+ :req_id => req_id,
84
+ :path => path,
85
+ :data => String.from_java_bytes(data),
86
+ :stat => stat.to_hash,
87
+ })
88
+ end
89
+ end
90
+
91
+ class StringCallback < Callback
92
+ include JZK::AsyncCallback::StringCallback
93
+
94
+ def processResult(rc, path, queue, str)
95
+ queue.push(:rc => rc, :req_id => req_id, :path => path, :string => str)
96
+ end
97
+ end
98
+
99
+ class StatCallback < Callback
100
+ include JZK::AsyncCallback::StatCallback
101
+
102
+ def processResult(rc, path, queue, stat)
103
+ logger.debug { "StatCallback#processResult rc: #{rc.inspect}, path: #{path.inspect}, queue: #{queue.inspect}, stat: #{stat.inspect}" }
104
+ queue.push(:rc => rc, :req_id => req_id, :stat => (stat and stat.to_hash), :path => path)
105
+ end
106
+ end
107
+
108
+ class Children2Callback < Callback
109
+ include JZK::AsyncCallback::Children2Callback
110
+
111
+ def processResult(rc, path, queue, children, stat)
112
+ queue.push(:rc => rc, :req_id => req_id, :path => path, :strings => children.to_a, :stat => stat.to_hash)
113
+ end
114
+ end
115
+
116
+ class ACLCallback < Callback
117
+ include JZK::AsyncCallback::ACLCallback
118
+
119
+ def processResult(rc, path, queue, acl, stat)
120
+ logger.debug { "ACLCallback#processResult #{rc.inspect} #{path.inspect} #{queue.inspect} #{acl.inspect} #{stat.inspect}" }
121
+ a = Array(acl).map { |a| a.to_hash }
122
+ queue.push(:rc => rc, :req_id => req_id, :path => path, :acl => a, :stat => stat.to_hash)
123
+ end
124
+ end
125
+
126
+ class VoidCallback < Callback
127
+ include JZK::AsyncCallback::VoidCallback
128
+
129
+ def processResult(rc, path, queue)
130
+ queue.push(:rc => rc, :req_id => req_id, :path => path)
131
+ end
132
+ end
133
+
134
+ class WatcherCallback < Callback
135
+ include JZK::Watcher
136
+
137
+ def initialize(event_queue)
138
+ @event_queue = event_queue
139
+ super(ZookeeperBase::ZKRB_GLOBAL_CB_REQ)
140
+ end
141
+
142
+ def process(event)
143
+ logger.debug { "WatcherCallback got event: #{event.to_hash}" }
144
+ hash = event.to_hash.merge(:req_id => req_id)
145
+ @event_queue.push(hash)
146
+ end
147
+ end
148
+ end
149
+
150
+ def reopen(timeout=10, watcher=nil)
151
+ watcher ||= @default_watcher
152
+
153
+ @req_mutex.synchronize do
154
+ # flushes all outstanding watcher reqs.
155
+ @watcher_req = {}
156
+ set_default_global_watcher(&watcher)
157
+ end
158
+
159
+ @jzk = JZK::ZooKeeper.new(@host, DEFAULT_SESSION_TIMEOUT, JavaCB::WatcherCallback.new(@event_queue))
160
+
161
+ if timeout > 0
162
+ time_to_stop = Time.now + timeout
163
+ until connected?
164
+ break if Time.now > time_to_stop
165
+ sleep 0.1
166
+ end
167
+ end
168
+
169
+ state
170
+ end
171
+
172
+ def initialize(host, timeout=10, watcher=nil)
173
+ @host = host
174
+ @event_queue = Queue.new
175
+ @current_req_id = 0
176
+ @req_mutex = Monitor.new
177
+ @watcher_reqs = {}
178
+ @completion_reqs = {}
179
+
180
+ watcher ||= get_default_global_watcher
181
+
182
+ reopen(timeout, watcher)
183
+ return nil unless connected?
184
+ setup_dispatch_thread!
185
+ end
186
+
187
+ def state
188
+ @jzk.state
189
+ end
190
+
191
+ def connected?
192
+ state == JZK::ZooKeeper::States::CONNECTED
193
+ end
194
+
195
+ def connecting?
196
+ state == JZK::ZooKeeper::States::CONNECTING
197
+ end
198
+
199
+ def associating?
200
+ state == JZK::ZooKeeper::States::ASSOCIATING
201
+ end
202
+
203
+ def get(req_id, path, callback, watcher)
204
+ handle_keeper_exception do
205
+ watch_cb = watcher ? create_watcher(req_id, path) : false
206
+
207
+ if callback
208
+ @jzk.getData(path, watch_cb, JavaCB::DataCallback.new(req_id), @event_queue)
209
+ [Code::Ok, nil, nil] # the 'nil, nil' isn't strictly necessary here
210
+ else # sync
211
+ stat = JZKD::Stat.new
212
+ data = String.from_java_bytes(@jzk.getData(path, watch_cb, stat))
213
+
214
+ [Code::Ok, data, stat.to_hash]
215
+ end
216
+ end
217
+ end
218
+
219
+ def set(req_id, path, data, callback, version)
220
+ handle_keeper_exception do
221
+ version ||= ANY_VERSION
222
+
223
+ if callback
224
+ @jzk.setData(path, data.to_java_bytes, version, JavaCB::StatCallback.new(req_id), @event_queue)
225
+ [Code::Ok, nil]
226
+ else
227
+ stat = @jzk.setData(path, data.to_java_bytes, version).to_hash
228
+ [Code::Ok, stat]
229
+ end
230
+ end
231
+ end
232
+
233
+ def get_children(req_id, path, callback, watcher)
234
+ handle_keeper_exception do
235
+ watch_cb = watcher ? create_watcher(req_id, path) : false
236
+
237
+ if callback
238
+ @jzk.getChildren(path, watch_cb, JavaCB::Children2Callback.new(req_id), @event_queue)
239
+ [Code::Ok, nil, nil]
240
+ else
241
+ stat = JZKD::Stat.new
242
+ children = @jzk.getChildren(path, watch_cb, stat)
243
+ [Code::Ok, children.to_a, stat.to_hash]
244
+ end
245
+ end
246
+ end
247
+
248
+ def create(req_id, path, data, callback, acl, flags)
249
+ handle_keeper_exception do
250
+ acl = Array(acl).map{ |a| JZKD::ACL.from_ruby_acl(a) }
251
+ mode = JZK::CreateMode.fromFlag(flags)
252
+
253
+ data ||= ''
254
+
255
+ if callback
256
+ @jzk.create(path, data.to_java_bytes, acl, mode, JavaCB::StringCallback.new(req_id), @event_queue)
257
+ [Code::Ok, nil]
258
+ else
259
+ new_path = @jzk.create(path, data.to_java_bytes, acl, mode)
260
+ [Code::Ok, new_path]
261
+ end
262
+ end
263
+ end
264
+
265
+ def delete(req_id, path, version, callback)
266
+ handle_keeper_exception do
267
+ if callback
268
+ @jzk.delete(path, version, JavaCB::VoidCallback.new(req_id), @event_queue)
269
+ else
270
+ @jzk.delete(path, version)
271
+ end
272
+
273
+ Code::Ok
274
+ end
275
+ end
276
+
277
+ def set_acl(req_id, path, acl, callback, version)
278
+ handle_keeper_exception do
279
+ logger.debug { "set_acl: acl #{acl.inspect}" }
280
+ acl = Array(acl).flatten.map { |a| JZKD::ACL.from_ruby_acl(a) }
281
+ logger.debug { "set_acl: converted #{acl.inspect}" }
282
+
283
+ if callback
284
+ @jzk.setACL(path, acl, version, JavaCB::ACLCallback.new(req_id), @event_queue)
285
+ else
286
+ @jzk.setACL(path, acl, version)
287
+ end
288
+
289
+ Code::Ok
290
+ end
291
+ end
292
+
293
+ def exists(req_id, path, callback, watcher)
294
+ handle_keeper_exception do
295
+ watch_cb = watcher ? create_watcher(req_id, path) : false
296
+
297
+ if callback
298
+ @jzk.exists(path, watch_cb, JavaCB::StatCallback.new(req_id), @event_queue)
299
+ [Code::Ok, nil, nil]
300
+ else
301
+ stat = @jzk.exists(path, watch_cb)
302
+ [Code::Ok, (stat and stat.to_hash)]
303
+ end
304
+ end
305
+ end
306
+
307
+ def get_acl(req_id, path, callback)
308
+ handle_keeper_exception do
309
+ stat = JZKD::Stat.new
310
+
311
+ if callback
312
+ logger.debug { "calling getACL, path: #{path.inspect}, stat: #{stat.inspect}" }
313
+ @jzk.getACL(path, stat, JavaCB::ACLCallback.new(req_id), @event_queue)
314
+ [Code::Ok, nil, nil]
315
+ else
316
+ acls = @jzk.getACL(path, stat).map { |a| a.to_hash }
317
+
318
+ [Code::Ok, Array(acls).map{|m| m.to_hash}, stat.to_hash]
319
+ end
320
+ end
321
+ end
322
+
323
+ def assert_open
324
+ # XXX don't know how to check for valid session state!
325
+ raise ZookeeperException::ConnectionClosed unless connected?
326
+ end
327
+
328
+ KILL_TOKEN = :__kill_token__
329
+
330
+ class DispatchShutdownException < StandardError; end
331
+
332
+ def close
333
+ @req_mutex.synchronize do
334
+ if @jzk
335
+ @jzk.close
336
+ wait_until { !connected? }
337
+ end
338
+
339
+ if @dispatcher
340
+ @dispatcher[:running] = false
341
+ @event_queue.push(KILL_TOKEN) # ignored by dispatch_next_callback
342
+ end
343
+ end
344
+
345
+ @dispatcher.join
346
+ end
347
+
348
+ # set the watcher object/proc that will receive all global events (such as session/state events)
349
+ #---
350
+ # XXX: this code needs to be duplicated from ext/zookeeper_base.rb because
351
+ # it's called from the initializer, and because of the C impl. we can't have
352
+ # the two decend from a common base, and a module wouldn't work
353
+ def set_default_global_watcher(&block)
354
+ @req_mutex.synchronize do
355
+ @default_watcher = block
356
+ @watcher_reqs[ZKRB_GLOBAL_CB_REQ] = { :watcher => @default_watcher, :watcher_context => nil }
357
+ end
358
+ end
359
+
360
+ protected
361
+ def handle_keeper_exception
362
+ yield
363
+ rescue JZK::KeeperException => e
364
+ e.cause.code.intValue
365
+ end
366
+
367
+ def call_type(callback, watcher)
368
+ if callback
369
+ watcher ? :async_watch : :async
370
+ else
371
+ watcher ? :sync_watch : :sync
372
+ end
373
+ end
374
+
375
+ def create_watcher(req_id, path)
376
+ lambda do |event|
377
+ h = { :req_id => req_id, :type => event.type.int_value, :state => event.state.int_value, :path => path }
378
+ @event_queue.push(h)
379
+ end
380
+ end
381
+
382
+ def get_next_event
383
+ @event_queue.pop.tap do |event|
384
+ raise DispatchShutdownException if event == KILL_TOKEN
385
+ end
386
+ end
387
+
388
+ # method to wait until block passed returns true or timeout (default is 10 seconds) is reached
389
+ def wait_until(timeout=10, &block)
390
+ time_to_stop = Time.now + timeout
391
+ until yield do
392
+ break if Time.now > time_to_stop
393
+ sleep 0.3
394
+ end
395
+ end
396
+
397
+ protected
398
+ # TODO: Make all global puts configurable
399
+ def get_default_global_watcher
400
+ Proc.new { |args|
401
+ logger.debug { "Ruby ZK Global CB called type=#{event_by_value(args[:type])} state=#{state_by_value(args[:state])}" }
402
+ true
403
+ }
404
+ end
405
+
406
+ private
407
+ def setup_dispatch_thread!
408
+ logger.debug { "starting dispatch thread" }
409
+ @dispatcher = Thread.new do
410
+ Thread.current[:running] = true
411
+
412
+ while Thread.current[:running]
413
+ begin
414
+ dispatch_next_callback
415
+ rescue DispatchShutdownException
416
+ logger.info { "dispatch thread exiting, got shutdown exception" }
417
+ break
418
+ rescue Exception => e
419
+ $stderr.puts ["#{e.class}: #{e.message}", e.backtrace.map { |n| "\t#{n}" }.join("\n")].join("\n")
420
+ end
421
+ end
422
+ end
423
+ end
424
+ end
425
+
426
+