zookeeper 0.9.3-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. data/.gitignore +10 -0
  2. data/CHANGELOG +119 -0
  3. data/Gemfile +17 -0
  4. data/LICENSE +23 -0
  5. data/Manifest +29 -0
  6. data/README.markdown +59 -0
  7. data/Rakefile +139 -0
  8. data/examples/cloud_config.rb +125 -0
  9. data/ext/.gitignore +6 -0
  10. data/ext/Rakefile +51 -0
  11. data/ext/c_zookeeper.rb +212 -0
  12. data/ext/dbg.h +53 -0
  13. data/ext/depend +5 -0
  14. data/ext/extconf.rb +85 -0
  15. data/ext/generate_gvl_code.rb +316 -0
  16. data/ext/zkc-3.3.5.tar.gz +0 -0
  17. data/ext/zkrb_wrapper.c +731 -0
  18. data/ext/zkrb_wrapper.h +330 -0
  19. data/ext/zkrb_wrapper_compat.c +15 -0
  20. data/ext/zkrb_wrapper_compat.h +11 -0
  21. data/ext/zookeeper_base.rb +211 -0
  22. data/ext/zookeeper_c.c +725 -0
  23. data/ext/zookeeper_lib.c +677 -0
  24. data/ext/zookeeper_lib.h +172 -0
  25. data/java/zookeeper_base.rb +477 -0
  26. data/lib/zookeeper.rb +297 -0
  27. data/lib/zookeeper/acls.rb +40 -0
  28. data/lib/zookeeper/callbacks.rb +91 -0
  29. data/lib/zookeeper/common.rb +174 -0
  30. data/lib/zookeeper/common/queue_with_pipe.rb +78 -0
  31. data/lib/zookeeper/constants.rb +57 -0
  32. data/lib/zookeeper/em_client.rb +55 -0
  33. data/lib/zookeeper/exceptions.rb +100 -0
  34. data/lib/zookeeper/stat.rb +21 -0
  35. data/lib/zookeeper/version.rb +6 -0
  36. data/notes.txt +14 -0
  37. data/spec/c_zookeeper_spec.rb +50 -0
  38. data/spec/chrooted_connection_spec.rb +81 -0
  39. data/spec/default_watcher_spec.rb +41 -0
  40. data/spec/em_spec.rb +51 -0
  41. data/spec/log4j.properties +17 -0
  42. data/spec/shared/all_success_return_values.rb +10 -0
  43. data/spec/shared/connection_examples.rb +1018 -0
  44. data/spec/spec_helper.rb +119 -0
  45. data/spec/support/progress_formatter.rb +15 -0
  46. data/spec/zookeeper_spec.rb +24 -0
  47. data/test/test_basic.rb +37 -0
  48. data/test/test_callback1.rb +36 -0
  49. data/test/test_close.rb +16 -0
  50. data/test/test_esoteric.rb +7 -0
  51. data/test/test_watcher1.rb +56 -0
  52. data/test/test_watcher2.rb +52 -0
  53. metadata +181 -0
@@ -0,0 +1,78 @@
1
+ module ZookeeperCommon
2
+ # Ceci n'est pas une pipe
3
+ class QueueWithPipe
4
+ extend Forwardable
5
+
6
+ def_delegators :@queue, :clear
7
+
8
+ # raised when close has been called, and pop() is performed
9
+ #
10
+ class ShutdownException < StandardError; end
11
+
12
+ # @private
13
+ KILL_TOKEN = Object.new unless defined?(KILL_TOKEN)
14
+
15
+ def initialize
16
+ # r, w = IO.pipe
17
+ # @pipe = { :read => r, :write => w }
18
+ @queue = Queue.new
19
+
20
+ # with the EventMachine client, we want to let EM handle clearing the
21
+ # event pipe, so we set this to false
22
+ # @clear_reads_on_pop = true
23
+
24
+ @mutex = Mutex.new
25
+ @closed = false
26
+ @graceful = false
27
+ end
28
+
29
+ def push(obj)
30
+ logger.debug { "#{self.class}##{__method__} obj: #{obj.inspect}, kill_token? #{obj == KILL_TOKEN}" }
31
+ @queue.push(obj)
32
+ end
33
+
34
+ def pop(non_blocking=false)
35
+ raise ShutdownException if closed? # this may get us in trouble
36
+
37
+ rv = @queue.pop(non_blocking)
38
+
39
+ if rv == KILL_TOKEN
40
+ close
41
+ raise ShutdownException
42
+ end
43
+
44
+ rv
45
+ end
46
+
47
+ # close the queue and causes ShutdownException to be raised on waiting threads
48
+ def graceful_close!
49
+ @mutex.synchronize do
50
+ return if @graceful or @closed
51
+ logger.debug { "#{self.class}##{__method__} gracefully closing" }
52
+ @graceful = true
53
+ push(KILL_TOKEN)
54
+ end
55
+ nil
56
+ end
57
+
58
+ def close
59
+ @mutex.synchronize do
60
+ return if @closed
61
+ @closed = true
62
+ end
63
+ end
64
+
65
+ def closed?
66
+ @mutex.synchronize { !!@closed }
67
+ end
68
+
69
+ private
70
+ def clear_reads_on_pop?
71
+ @clear_reads_on_pop
72
+ end
73
+
74
+ def logger
75
+ Zookeeper.logger
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,57 @@
1
+ module ZookeeperConstants
2
+ # file type masks
3
+ ZOO_EPHEMERAL = 1
4
+ ZOO_SEQUENCE = 2
5
+
6
+ # session state
7
+ ZOO_EXPIRED_SESSION_STATE = -112
8
+ ZOO_AUTH_FAILED_STATE = -113
9
+ ZOO_CLOSED_STATE = 0
10
+ ZOO_CONNECTING_STATE = 1
11
+ ZOO_ASSOCIATING_STATE = 2
12
+ ZOO_CONNECTED_STATE = 3
13
+
14
+ # watch types
15
+ ZOO_CREATED_EVENT = 1
16
+ ZOO_DELETED_EVENT = 2
17
+ ZOO_CHANGED_EVENT = 3
18
+ ZOO_CHILD_EVENT = 4
19
+ ZOO_SESSION_EVENT = -1
20
+ ZOO_NOTWATCHING_EVENT = -2
21
+
22
+ # only used by the C extension
23
+ ZOO_LOG_LEVEL_ERROR = 1
24
+ ZOO_LOG_LEVEL_WARN = 2
25
+ ZOO_LOG_LEVEL_INFO = 3
26
+ ZOO_LOG_LEVEL_DEBUG = 4
27
+
28
+ # used to find the name for a numeric event
29
+ # @private
30
+ EVENT_TYPE_NAMES = {
31
+ 1 => 'created',
32
+ 2 => 'deleted',
33
+ 3 => 'changed',
34
+ 4 => 'child',
35
+ -1 => 'session',
36
+ -2 => 'notwatching',
37
+ }
38
+
39
+ # used to pretty print the state name
40
+ # @private
41
+ STATE_NAMES = {
42
+ -112 => 'expired_session',
43
+ -113 => 'auth_failed',
44
+ 0 => 'closed',
45
+ 1 => 'connecting',
46
+ 2 => 'associating',
47
+ 3 => 'connected',
48
+ }
49
+
50
+ def event_by_value(v)
51
+ (name = EVENT_TYPE_NAMES[v]) ? "ZOO_#{name.upcase}_EVENT" : ''
52
+ end
53
+
54
+ def state_by_value(v)
55
+ (name = STATE_NAMES[v]) ? "ZOO_#{name.upcase}_STATE" : ''
56
+ end
57
+ end
@@ -0,0 +1,55 @@
1
+ require 'zookeeper'
2
+ require 'eventmachine'
3
+
4
+ module ZookeeperEM
5
+ class Client < Zookeeper
6
+ # @private
7
+ # the EM Connection instance we receive once we call EM.watch on our selectable_io
8
+ attr_reader :em_connection
9
+
10
+ def initialize(*a, &b)
11
+ @on_close = EM::DefaultDeferrable.new
12
+ @on_attached = EM::DefaultDeferrable.new
13
+ @em_connection = nil
14
+ logger.debug { "ZookeeperEM::Client obj_id %x: init" % [object_id] }
15
+ super(*a, &b)
16
+ on_attached.succeed
17
+ end
18
+
19
+ # EM::DefaultDeferrable that will be called back when our em_connection has been detached
20
+ # and we've completed the close operation
21
+ def on_close(&block)
22
+ @on_close.callback(&block) if block
23
+ @on_close
24
+ end
25
+
26
+ # called after we've successfully registered our selectable_io to be
27
+ # managed by the EM reactor
28
+ def on_attached(&block)
29
+ @on_attached.callback(&block) if block
30
+ @on_attached
31
+ end
32
+
33
+ def dispatch_next_callback(hash)
34
+ EM.schedule do
35
+ if running? and not closed?
36
+ logger.debug { "#{self.class}##{__method__} dispatch_next_callback: #{hash.inspect}: reactor_thread? #{EM.reactor_thread?}, running? #{running?}, closed? #{closed?}" }
37
+ super(hash)
38
+ end
39
+ end
40
+ end
41
+
42
+ def close(&block)
43
+ on_close(&block)
44
+ super()
45
+ on_close.succeed
46
+ end
47
+
48
+ # Because eventmachine is single-threaded, and events are dispatched on the
49
+ # reactor thread we just delegate this to EM.reactor_thread?
50
+ def event_dispatch_thread?
51
+ EM.reactor_thread?
52
+ end
53
+ end # Client
54
+ end # ZookeeperEM
55
+
@@ -0,0 +1,100 @@
1
+ module ZookeeperExceptions
2
+ # exceptions/errors
3
+ ZOK = 0
4
+ ZSYSTEMERROR = -1
5
+ ZRUNTIMEINCONSISTENCY = -2
6
+ ZDATAINCONSISTENCY = -3
7
+ ZCONNECTIONLOSS = -4
8
+ ZMARSHALLINGERROR = -5
9
+ ZUNIMPLEMENTED = -6
10
+ ZOPERATIONTIMEOUT = -7
11
+ ZBADARGUMENTS = -8
12
+ ZINVALIDSTATE = -9
13
+
14
+ # api errors
15
+ ZAPIERROR = -100
16
+ ZNONODE = -101
17
+ ZNOAUTH = -102
18
+ ZBADVERSION = -103
19
+ ZNOCHILDRENFOREPHEMERALS = -108
20
+ ZNODEEXISTS = -110
21
+ ZNOTEMPTY = -111
22
+ ZSESSIONEXPIRED = -112
23
+ ZINVALIDCALLBACK = -113
24
+ ZINVALIDACL = -114
25
+ ZAUTHFAILED = -115
26
+ ZCLOSING = -116
27
+ ZNOTHING = -117
28
+ ZSESSIONMOVED = -118
29
+
30
+ class ZookeeperException < StandardError
31
+ class EverythingOk < ZookeeperException; end
32
+ class SystemError < ZookeeperException; end
33
+ class RunTimeInconsistency < ZookeeperException; end
34
+ class DataInconsistency < ZookeeperException; end
35
+ class ConnectionLoss < ZookeeperException; end
36
+ class MarshallingError < ZookeeperException; end
37
+ class Unimplemented < ZookeeperException; end
38
+ class OperationTimeOut < ZookeeperException; end
39
+ class BadArguments < ZookeeperException; end
40
+ class InvalidState < ZookeeperException; end
41
+ class ApiError < ZookeeperException; end
42
+ class NoNode < ZookeeperException; end
43
+ class NoAuth < ZookeeperException; end
44
+ class BadVersion < ZookeeperException; end
45
+ class NoChildrenForEphemerals < ZookeeperException; end
46
+ class NodeExists < ZookeeperException; end
47
+ class NotEmpty < ZookeeperException; end
48
+ class SessionExpired < ZookeeperException; end
49
+ class InvalidCallback < ZookeeperException; end
50
+ class InvalidACL < ZookeeperException; end
51
+ class AuthFailed < ZookeeperException; end
52
+ class Closing < ZookeeperException; end
53
+ class Nothing < ZookeeperException; end
54
+ class SessionMoved < ZookeeperException; end
55
+
56
+ # these are Ruby client exceptions
57
+ class ConnectionClosed < ZookeeperException; end
58
+ class NotConnected < ZookeeperException; end
59
+ class ShuttingDownException < ZookeeperException; end
60
+ class DataTooLargeException < ZookeeperException; end
61
+
62
+ # yes, make an alias, this is the way zookeeper refers to it
63
+ ExpiredSession = SessionExpired
64
+
65
+ def self.by_code(code)
66
+ case code
67
+ when ZOK then EverythingOk
68
+ when ZSYSTEMERROR then SystemError
69
+ when ZRUNTIMEINCONSISTENCY then RunTimeInconsistency
70
+ when ZDATAINCONSISTENCY then DataInconsistency
71
+ when ZCONNECTIONLOSS then ConnectionLoss
72
+ when ZMARSHALLINGERROR then MarshallingError
73
+ when ZUNIMPLEMENTED then Unimplemented
74
+ when ZOPERATIONTIMEOUT then OperationTimeOut
75
+ when ZBADARGUMENTS then BadArguments
76
+ when ZINVALIDSTATE then InvalidState
77
+ when ZAPIERROR then ApiError
78
+ when ZNONODE then NoNode
79
+ when ZNOAUTH then NoAuth
80
+ when ZBADVERSION then BadVersion
81
+ when ZNOCHILDRENFOREPHEMERALS then NoChildrenForEphemerals
82
+ when ZNODEEXISTS then NodeExists
83
+ when ZNOTEMPTY then NotEmpty
84
+ when ZSESSIONEXPIRED then SessionExpired
85
+ when ZINVALIDCALLBACK then InvalidCallback
86
+ when ZINVALIDACL then InvalidACL
87
+ when ZAUTHFAILED then AuthFailed
88
+ when ZCLOSING then Closing
89
+ when ZNOTHING then Nothing
90
+ when ZSESSIONMOVED then SessionMoved
91
+ else Exception.new("no exception defined for code #{code}")
92
+ end
93
+ end
94
+
95
+ def self.raise_on_error(code)
96
+ exc = self.by_code(code)
97
+ raise exc unless exc == EverythingOk
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,21 @@
1
+ module ZookeeperStat
2
+ class Stat
3
+ attr_reader :version, :exists, :czxid, :mzxid, :ctime, :mtime, :cversion, :aversion, :ephemeralOwner, :dataLength, :numChildren, :pzxid
4
+
5
+ alias :ephemeral_owner :ephemeralOwner
6
+ alias :num_children :numChildren
7
+ alias :data_length :dataLength
8
+
9
+ def initialize(val)
10
+ @exists = !!val
11
+ @czxid, @mzxid, @ctime, @mtime, @version, @cversion, @aversion,
12
+ @ephemeralOwner, @dataLength, @numChildren, @pzxid = val if val.is_a?(Array)
13
+ val.each { |k,v| instance_variable_set "@#{k}", v } if val.is_a?(Hash)
14
+ raise ArgumentError unless (val.is_a?(Hash) or val.is_a?(Array) or val.nil?)
15
+ end
16
+
17
+ def exists?
18
+ @exists
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,6 @@
1
+ # this "namespacing" is retarded. fix this in 1.0
2
+ # @private
3
+ module ZookeeperVersion
4
+ VERSION = '0.9.3'
5
+ DRIVER_VERSION = '3.3.5'
6
+ end
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
+