zkruby 3.4.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,108 @@
1
+
2
+ # Including in a class and us the "enum" DSL method to create class instances
3
+ # As a special case, if the including class is descendant from StandardError, the enumerated
4
+ # instances will be classes descendent from the including class (eg like Errno)
5
+ module Enumeration
6
+
7
+ def self.included(base)
8
+ base.extend(ClassMethods)
9
+ # In the normal case we just include our methods in the base class
10
+ # but for Errors we need to include them on each instance
11
+ base.send :include,InstanceMethods unless base < StandardError
12
+ end
13
+
14
+ module InstanceMethods
15
+ def === (other)
16
+ case other
17
+ when Symbol
18
+ to_sym == other
19
+ when Fixnum
20
+ to_int == other
21
+ else
22
+ super
23
+ end
24
+ end
25
+
26
+ def to_i
27
+ to_int
28
+ end
29
+
30
+ def to_int
31
+ @index
32
+ end
33
+
34
+ def |(num)
35
+ to_int | num
36
+ end
37
+
38
+ def &(num)
39
+ to_int & num
40
+ end
41
+
42
+ def to_sym
43
+ @name
44
+ end
45
+
46
+ def to_s
47
+ "#{super} (:#{@name} [#{@index}])"
48
+ end
49
+ end
50
+
51
+ module ClassMethods
52
+
53
+ # @param [] ref Symbol, Fixnum or Enumeration
54
+ # @return [Enumeration] instance representing ref or nil if not found
55
+ def get(ref)
56
+ @enums[ref]
57
+ end
58
+
59
+ # @param [] ref Symbol, Fixnum
60
+ # @raise [KeyError] if ref not found
61
+ # @return [Enumeration]
62
+ def fetch(ref)
63
+ @enums.fetch(ref)
64
+ end
65
+
66
+ # Will dynamically create a new enum instance if ref is not found
67
+ def lookup(ref)
68
+ case ref
69
+ when Enumeration,Class
70
+ ref
71
+ when Symbol
72
+ get(ref) || enum(ref,nil)
73
+ when Fixnum
74
+ get(ref) || enum(nil,ref)
75
+ else
76
+ nil
77
+ end
78
+ end
79
+
80
+ def enum(name,index,*args)
81
+ @enums ||= {}
82
+
83
+ instance = if (self < StandardError) then Class.new(self) else self.new(*args) end
84
+
85
+ instance.extend(InstanceMethods) if (self < StandardError)
86
+
87
+ const_name = if name.nil?
88
+ "ENUM" + index.to_s.sub("-","_")
89
+ else
90
+ name.to_s.upcase
91
+ end
92
+
93
+ self.const_set(const_name.to_s.upcase,instance)
94
+
95
+ @enums[instance] = instance
96
+ @enums[name] = instance if name
97
+ @enums[index] = instance if index
98
+ instance.instance_variable_set(:@name,name)
99
+ instance.instance_variable_set(:@index,index)
100
+ instance.freeze
101
+ end
102
+
103
+ def method_missing(method,*args)
104
+ @enums[method.downcase] || super
105
+ end
106
+
107
+ end
108
+ end
@@ -0,0 +1,186 @@
1
+ require 'eventmachine'
2
+
3
+ if defined?(JRUBY_VERSION) && JRUBY_VERSION =~ /1\.6\.5.*/
4
+ raise "Fibers are broken in JRuby 1.6.5 (See JRUBY-6170)"
5
+ end
6
+
7
+ require 'strand'
8
+
9
+ module ZooKeeper
10
+ module EventMachine
11
+
12
+ class ClientConn < ::EM::Connection
13
+ include Protocol
14
+ include Slf4r::Logger
15
+
16
+ unless EventMachine.methods.include?(:set_pending_connect_timeout)
17
+ def set_pending_connect_timeout(timeout)
18
+
19
+ end
20
+ end
21
+
22
+ def initialize(session,connect_timeout)
23
+ @session = session
24
+ @connect_timeout = connect_timeout
25
+ set_pending_connect_timeout(connect_timeout)
26
+ rescue Exception => ex
27
+ logger.warn("Exception in initialize",ex)
28
+ end
29
+
30
+ def post_init()
31
+ rescue Exception => ex
32
+ logger.warn("Exception in post_init",ex)
33
+ end
34
+
35
+ def connection_completed()
36
+ @session.prime_connection(self)
37
+
38
+ # Make sure we connect within the timeout period
39
+ # TODO this should really be the amount of connect timeout left over
40
+ @timer = EM.add_timer(@connect_timeout) do
41
+ if @session.connected?
42
+ # Start the ping timer
43
+ ping = @session.ping_interval
44
+ @timer = EM.add_periodic_timer ( ping ) do
45
+ case @ping
46
+ when 1 then @session.ping()
47
+ when 2 then close_connection()
48
+ end
49
+ @ping += 1
50
+ end
51
+ else
52
+ close_connection()
53
+ end
54
+ end
55
+
56
+ rescue Exception => ex
57
+ logger.warn("Exception in connection_completed",ex)
58
+ end
59
+
60
+ def receive_records(packet_io)
61
+ @ping = 0
62
+ @session.receive_records(packet_io)
63
+ end
64
+
65
+ def disconnect()
66
+ close_connection()
67
+ end
68
+
69
+ def unbind
70
+ EM.cancel_timer(@timer) if @timer
71
+ @session.disconnected()
72
+ rescue Exception => ex
73
+ logger.warn("Exception in unbind",ex)
74
+ end
75
+
76
+ end
77
+
78
+
79
+ # The EventMachine binding is very simple because there is only one thread!
80
+ # and we have good stuff like timers provided for us
81
+ class Binding
82
+ include Slf4r::Logger
83
+ # We can use this binding if we are running in the reactor thread
84
+ def self.available?()
85
+ EM.reactor_running? && EM.reactor_thread?
86
+ end
87
+
88
+ def self.context(&context_block)
89
+ s = Strand.new() do
90
+ context_block.call(Strand)
91
+ end
92
+ s.join
93
+ end
94
+
95
+ attr_reader :client, :session
96
+ def start(client,session)
97
+ @client = client
98
+ @session = session
99
+ @session.start()
100
+ end
101
+
102
+ def connect(host,port,delay,timeout)
103
+ EM.add_timer(delay) do
104
+ EM.connect(host,port,ZooKeeper::EventMachine::ClientConn,@session,timeout)
105
+ end
106
+ end
107
+
108
+ # You are working in event machine it is up to you to ensure your callbacks do not block
109
+ def invoke(callback,*args)
110
+ callback.call(*args)
111
+ end
112
+
113
+ def queue_request(*args,&callback)
114
+ op = AsyncOp.new(self,&callback)
115
+ begin
116
+ @session.queue_request(*args) do |error,response|
117
+ op.resume(error,response)
118
+ end
119
+ rescue ZooKeeper::Error => ex
120
+ op.resume(ex,nil)
121
+ end
122
+
123
+ op
124
+ end
125
+
126
+ def close(&callback)
127
+
128
+ op = AsyncOp.new(self,&callback)
129
+
130
+ begin
131
+ @session.close() do |error,response|
132
+ op.resume(error,response)
133
+ end
134
+ rescue ZooKeeper::Error => ex
135
+ op.resume(ex,nil)
136
+ end
137
+
138
+ op
139
+ end
140
+
141
+ end #class Binding
142
+
143
+ class AsyncOp < ZooKeeper::AsyncOp
144
+
145
+ def initialize(binding,&callback)
146
+ @em_binding = binding
147
+
148
+ # Wrap the callback in its own Strand
149
+ @op_strand = Strand.new do
150
+ #immediately pause
151
+ error, response = Strand.yield
152
+ Strand.current[ZooKeeper::CURRENT] = [ @em_binding.client ]
153
+ raise error if error
154
+ callback.call(response)
155
+ end
156
+ end
157
+
158
+ def resume(error,response)
159
+ #TODO - raise issue in strand for resume to take arguments
160
+ op_strand.fiber.resume(error,response)
161
+ end
162
+
163
+ private
164
+
165
+ attr_reader :op_strand,:err_strand
166
+
167
+ def set_error_handler(errcallback)
168
+ @err_strand = Strand.new() do
169
+ begin
170
+ op_strand.value()
171
+ rescue StandardError => ex
172
+ Strand.current[ZooKeeper::CURRENT] = [ @em_binding.client ]
173
+ errcallback.call(ex)
174
+ end
175
+ end
176
+ end
177
+
178
+ def wait_value()
179
+ err_strand ? err_strand.value : op_strand.value
180
+ end
181
+
182
+ end #class AsyncOp
183
+ end #module EventMachine
184
+ end #module ZooKeeper
185
+
186
+ ZooKeeper.add_binding(ZooKeeper::EventMachine::Binding)
@@ -0,0 +1,47 @@
1
+ module ZooKeeper
2
+
3
+ module Proto
4
+
5
+ #MultiTransactionRecord and MultiResponse in java
6
+ #are not able to be compiled from Jute because jute doesn't support
7
+ #discriminators
8
+ class MultiRequest < BinData::Record
9
+ array :requests, :read_until => lambda { element.header._type < 0 } do
10
+ multi_header :header
11
+ choice :request, :selection => lambda { header._type },
12
+ :onlyif => lambda { header._type > 0 } do
13
+ create_request 1
14
+ delete_request 2
15
+ set_data_request 5
16
+ check_version_request 13
17
+ end
18
+ end
19
+
20
+ end
21
+
22
+ class MultiResponseItem < BinData::Record
23
+ multi_header :header
24
+ choice :response, :selection => lambda { header._type },
25
+ :onlyif => lambda { has_response? } do
26
+ error_response -1
27
+ create_response 1
28
+ set_data_response 5
29
+ end
30
+
31
+ def has_response?
32
+ !done? && [ -1, 1, 5 ] .include?(header._type)
33
+ end
34
+
35
+ def done?
36
+ # Boolean's don't actually work properly in bindata
37
+ header.done == true
38
+ end
39
+
40
+ end
41
+
42
+ class MultiResponse < BinData::Record
43
+ array :responses, :type => :multi_response_item, :read_until => lambda { element.done? }
44
+ end
45
+ end
46
+ end
47
+
@@ -0,0 +1,182 @@
1
+ require 'stringio'
2
+
3
+ module ZooKeeper
4
+ # Represents a problem communicating with the ZK cluster
5
+ # client's shouldn't see this
6
+ class ProtocolError < IOError;end
7
+
8
+ # Raw object protocol, very similar to EM's native ObjectProtocol
9
+ # Expects:
10
+ # #receive_data and #send_records to be invoked
11
+ # #receive_records and #send_data to be implemented
12
+ module Protocol
13
+ MIN_PACKET = 5 #TODO Work out what the min packet size really is
14
+ include Slf4r::Logger
15
+
16
+ def receive_data data # :nodoc:
17
+
18
+ @buffer ||= StringIO.new()
19
+ @buffer.seek(0, IO::SEEK_END)
20
+ @buffer << data
21
+ @buffer.rewind
22
+ logger.debug { "Received #{data.length} bytes: buffer length = #{@buffer.length} pos = #{@buffer.pos}" }
23
+ loop do
24
+ if @buffer.length - @buffer.pos > MIN_PACKET
25
+ packet_size = @buffer.read(4).unpack("N").first
26
+ if (@buffer.length - @buffer.pos >= packet_size)
27
+ expected_pos = @buffer.pos + packet_size
28
+ # We just pass the buffer around and expect packet_size to be consumed
29
+ receive_records(@buffer)
30
+ if (@buffer.pos != expected_pos)
31
+ #this can happen during disconnection with left over packets
32
+ #the connection is dying anyway
33
+ leftover = @buffer.read(packet_size).unpack("H*")[0]
34
+ raise ProtocolError, "Records not consumed #{leftover}"
35
+ end
36
+ logger.debug { "Consumed packet #{packet_size}. Buffer pos=#{@buffer.pos}, length=#{@buffer.length}" }
37
+ next
38
+ else
39
+ # found the last partial packet
40
+ @buffer.seek(-4, IO::SEEK_CUR)
41
+ logger.debug { "Buffer contains #{@buffer.length} of #{packet_size} packet" }
42
+ end
43
+ end
44
+ break
45
+ end
46
+ # reset the buffer
47
+ @buffer = StringIO.new(@buffer.read()) if @buffer.pos > 0
48
+ end
49
+
50
+ def receive_records(packet_io)
51
+ #stub
52
+ #we don't unpack records here because we don't know what kind of records they are!
53
+ end
54
+
55
+ def send_records(*records)
56
+ length = 0
57
+ bindata = records.collect { |r| s = r.to_binary_s; length += s.length; s }
58
+ send_data([length].pack("N"))
59
+ bindata.each { |b| send_data(b) }
60
+ logger.debug { "Sent #{length} byte packet containing #{records.length} records" }
61
+ end
62
+ end
63
+
64
+ class Operation
65
+ attr_reader :op, :opcode, :request, :response, :callback
66
+ def initialize(op,opcode,request,response,callback)
67
+ @op=op;@opcode=opcode
68
+ @request=request;@response=response
69
+ @callback=callback
70
+ end
71
+
72
+ def path
73
+ #Every request has a path!
74
+ #TODO - path may be chrooted!
75
+ request.path if request.respond_to?(:path)
76
+ end
77
+ end
78
+
79
+ class Packet < Operation
80
+ attr_reader :xid, :watch_type, :watcher
81
+
82
+ def initialize(xid,op,opcode,request,response,watch_type,watcher,callback)
83
+ super(op,opcode,request,response,callback)
84
+ @xid=xid;
85
+ @watch_type = watch_type; @watcher = watcher
86
+ end
87
+
88
+
89
+ def error(reason)
90
+ result(reason)[0..2] # don't need the watch
91
+ end
92
+
93
+ def result(rc)
94
+ error = nil
95
+ unless (Error::NONE === rc) then
96
+ error = Error.lookup(rc)
97
+ error = error.exception("ZooKeeper error for #{@op}(#{path}) ")
98
+ end
99
+ [ callback, error ,response, watch_type ]
100
+ end
101
+ end
102
+
103
+ # NoNode error is expected for exists
104
+ class ExistsPacket < Packet
105
+ def result(rc)
106
+ Error::NO_NODE === rc ? [ callback, nil, nil, :exists ] : super(rc)
107
+ end
108
+ end
109
+
110
+ # In the normal case the close packet will be last and will get
111
+ # cleared via disconnected() and :session_expired
112
+ class ClosePacket < Packet
113
+ def result(rc)
114
+ Error::SESSION_EXPIRED == rc ? [ callback, nil, nil, nil ] : super(rc)
115
+ end
116
+ end
117
+
118
+
119
+ # Returned by asynchronous calls
120
+ #
121
+ # @example
122
+ # op = zk.stat("\apath") { |stat| something_with_stat() }
123
+ #
124
+ # op.on_error do |err|
125
+ # case err
126
+ # when ZK::Error::SESSION_EXPIRED
127
+ # puts "Ignoring session expired"
128
+ # else
129
+ # raise err
130
+ # end
131
+ # end
132
+ #
133
+ # begin
134
+ # result_of_somthing_with_stat = op.value
135
+ # rescue StandardError => ex
136
+ # puts "Oops, my error handler raised an exception"
137
+ # end
138
+ #
139
+ #
140
+ class AsyncOp
141
+
142
+ attr_accessor :backtrace
143
+
144
+ # Provide an error callback.
145
+ # @param block the error callback as a block
146
+ # @yieldparam [StandardError] the error raised by the zookeeper operation OR by its callback
147
+ def errback(&block)
148
+ set_error_handler(block)
149
+ end
150
+
151
+ # @param block the error callback as a Proc
152
+ def errback=(block)
153
+ set_error_handler(block)
154
+ end
155
+
156
+ alias :on_error :errback
157
+
158
+ # Wait for the async op to finish and returns its value
159
+ # will raise an exception if
160
+ # the operation itself fails and there is no error handler
161
+ # the callback raises a StandardError and there is no error handler
162
+ # the error handler raises StandardError
163
+ def value();
164
+ wait_value()
165
+ rescue ZooKeeper::Error => ex
166
+ # Set the backtrace to the original caller, rather than the ZK event loop
167
+ ex.set_backtrace(@backtrace) if @backtrace
168
+ raise ex
169
+ end
170
+
171
+ private
172
+
173
+ def set_error_handler(blk)
174
+ raise NotImplementedError, ":wait_result to be privately implemented by binding"
175
+ end
176
+
177
+ def wait_value();
178
+ raise NotImplementedError, ":wait_result to be privately implemented by binding"
179
+ end
180
+ end
181
+ end
182
+