slyphon-zookeeper 0.1.7-java → 0.2.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.
- data/.gitignore +1 -0
- data/ext/zookeeper_base.rb +46 -16
- data/ext/zookeeper_c.c +99 -16
- data/ext/zookeeper_lib.c +23 -7
- data/java/zookeeper_base.rb +154 -37
- data/lib/zookeeper/common.rb +7 -21
- data/lib/zookeeper/em_client.rb +135 -0
- data/lib/zookeeper.rb +72 -1
- data/slyphon-zookeeper.gemspec +2 -2
- data/spec/em_spec.rb +138 -0
- data/spec/spec_helper.rb +19 -0
- metadata +141 -107
    
        data/java/zookeeper_base.rb
    CHANGED
    
    | @@ -59,6 +59,53 @@ class ZookeeperBase | |
| 59 59 | 
             
                end
         | 
| 60 60 | 
             
              end
         | 
| 61 61 |  | 
| 62 | 
            +
              class QueueWithPipe
         | 
| 63 | 
            +
                attr_writer :clear_reads_on_pop
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                def initialize
         | 
| 66 | 
            +
                  r, w = IO.pipe
         | 
| 67 | 
            +
                  @pipe = { :read => r, :write => w }
         | 
| 68 | 
            +
                  @queue = Queue.new
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                  # with the EventMachine client, we want to let EM handle clearing the
         | 
| 71 | 
            +
                  # event pipe, so we set this to false
         | 
| 72 | 
            +
                  @clear_reads_on_pop = true
         | 
| 73 | 
            +
                end
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                def push(obj)
         | 
| 76 | 
            +
                  rv = @queue.push(obj)
         | 
| 77 | 
            +
                  @pipe[:write].write('0')
         | 
| 78 | 
            +
                  logger.debug { "pushed #{obj.inspect} onto queue and wrote to pipe" }
         | 
| 79 | 
            +
                  rv
         | 
| 80 | 
            +
                end
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                def pop(non_blocking=false)
         | 
| 83 | 
            +
                  rv = @queue.pop(non_blocking)
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                  # if non_blocking is true and an exception is raised, this won't get called
         | 
| 86 | 
            +
                  @pipe[:read].read(1) if clear_reads_on_pop?
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                  rv
         | 
| 89 | 
            +
                end
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                def close
         | 
| 92 | 
            +
                  @pipe.values.each { |io| io.close unless io.closed? }
         | 
| 93 | 
            +
                end
         | 
| 94 | 
            +
             | 
| 95 | 
            +
                def selectable_io
         | 
| 96 | 
            +
                  @pipe[:read]
         | 
| 97 | 
            +
                end
         | 
| 98 | 
            +
             | 
| 99 | 
            +
                private
         | 
| 100 | 
            +
                  def clear_reads_on_pop?
         | 
| 101 | 
            +
                    @clear_reads_on_pop
         | 
| 102 | 
            +
                  end
         | 
| 103 | 
            +
             | 
| 104 | 
            +
                  def logger
         | 
| 105 | 
            +
                    Zookeeper.logger
         | 
| 106 | 
            +
                  end
         | 
| 107 | 
            +
              end
         | 
| 108 | 
            +
             | 
| 62 109 | 
             
              # used for internal dispatching
         | 
| 63 110 | 
             
              module JavaCB #:nodoc:
         | 
| 64 111 | 
             
                class Callback
         | 
| @@ -78,13 +125,24 @@ class ZookeeperBase | |
| 78 125 | 
             
                  include JZK::AsyncCallback::DataCallback
         | 
| 79 126 |  | 
| 80 127 | 
             
                  def processResult(rc, path, queue, data, stat)
         | 
| 81 | 
            -
                    queue. | 
| 128 | 
            +
                    logger.debug { "#{self.class.name}#processResult rc: #{rc}, req_id: #{req_id}, path: #{path}, queue: #{queue.inspect}, data: #{data.inspect}, stat: #{stat.inspect}" }
         | 
| 129 | 
            +
             | 
| 130 | 
            +
                    hash = {
         | 
| 82 131 | 
             
                      :rc     => rc,
         | 
| 83 132 | 
             
                      :req_id => req_id,
         | 
| 84 133 | 
             
                      :path   => path,
         | 
| 85 | 
            -
                      :data   => String.from_java_bytes(data),
         | 
| 86 | 
            -
                      :stat   => stat.to_hash,
         | 
| 87 | 
            -
                    } | 
| 134 | 
            +
                      :data   => (data && String.from_java_bytes(data)),
         | 
| 135 | 
            +
                      :stat   => (stat && stat.to_hash),
         | 
| 136 | 
            +
                    }
         | 
| 137 | 
            +
             | 
| 138 | 
            +
            #         if rc == Zookeeper::ZOK
         | 
| 139 | 
            +
            #           hash.merge!({
         | 
| 140 | 
            +
            #             :data   => String.from_java_bytes(data),
         | 
| 141 | 
            +
            #             :stat   => stat.to_hash,
         | 
| 142 | 
            +
            #           })
         | 
| 143 | 
            +
            #         end
         | 
| 144 | 
            +
             | 
| 145 | 
            +
                    queue.push(hash)
         | 
| 88 146 | 
             
                  end
         | 
| 89 147 | 
             
                end
         | 
| 90 148 |  | 
| @@ -92,6 +150,7 @@ class ZookeeperBase | |
| 92 150 | 
             
                  include JZK::AsyncCallback::StringCallback
         | 
| 93 151 |  | 
| 94 152 | 
             
                  def processResult(rc, path, queue, str)
         | 
| 153 | 
            +
                    logger.debug { "#{self.class.name}#processResult rc: #{rc}, req_id: #{req_id}, path: #{path}, queue: #{queue.inspect}, str: #{str.inspect}" }
         | 
| 95 154 | 
             
                    queue.push(:rc => rc, :req_id => req_id, :path => path, :string => str)
         | 
| 96 155 | 
             
                  end
         | 
| 97 156 | 
             
                end
         | 
| @@ -100,7 +159,7 @@ class ZookeeperBase | |
| 100 159 | 
             
                  include JZK::AsyncCallback::StatCallback
         | 
| 101 160 |  | 
| 102 161 | 
             
                  def processResult(rc, path, queue, stat)
         | 
| 103 | 
            -
                    logger.debug { " | 
| 162 | 
            +
                    logger.debug { "#{self.class.name}#processResult rc: #{rc.inspect}, req_id: #{req_id}, path: #{path.inspect}, queue: #{queue.inspect}, stat: #{stat.inspect}" }
         | 
| 104 163 | 
             
                    queue.push(:rc => rc, :req_id => req_id, :stat => (stat and stat.to_hash), :path => path)
         | 
| 105 164 | 
             
                  end
         | 
| 106 165 | 
             
                end
         | 
| @@ -109,7 +168,16 @@ class ZookeeperBase | |
| 109 168 | 
             
                  include JZK::AsyncCallback::Children2Callback
         | 
| 110 169 |  | 
| 111 170 | 
             
                  def processResult(rc, path, queue, children, stat)
         | 
| 112 | 
            -
                     | 
| 171 | 
            +
                    logger.debug { "#{self.class.name}#processResult rc: #{rc}, req_id: #{req_id}, path: #{path}, queue: #{queue.inspect}, children: #{children.inspect}, stat: #{stat.inspect}" }
         | 
| 172 | 
            +
                    hash = {
         | 
| 173 | 
            +
                      :rc       => rc, 
         | 
| 174 | 
            +
                      :req_id   => req_id, 
         | 
| 175 | 
            +
                      :path     => path, 
         | 
| 176 | 
            +
                      :strings  => (children && children.to_a), 
         | 
| 177 | 
            +
                      :stat     => (stat and stat.to_hash),
         | 
| 178 | 
            +
                    }
         | 
| 179 | 
            +
             | 
| 180 | 
            +
                    queue.push(hash)
         | 
| 113 181 | 
             
                  end
         | 
| 114 182 | 
             
                end
         | 
| 115 183 |  | 
| @@ -117,9 +185,9 @@ class ZookeeperBase | |
| 117 185 | 
             
                  include JZK::AsyncCallback::ACLCallback
         | 
| 118 186 |  | 
| 119 187 | 
             
                  def processResult(rc, path, queue, acl, stat)
         | 
| 120 | 
            -
                    logger.debug { "ACLCallback#processResult #{rc.inspect} #{path.inspect} #{queue.inspect} #{acl.inspect} #{stat.inspect}" }
         | 
| 188 | 
            +
                    logger.debug { "ACLCallback#processResult rc: #{rc.inspect}, req_id: #{req_id}, path: #{path.inspect}, queue: #{queue.inspect}, acl: #{acl.inspect}, stat: #{stat.inspect}" }
         | 
| 121 189 | 
             
                    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)
         | 
| 190 | 
            +
                    queue.push(:rc => rc, :req_id => req_id, :path => path, :acl => a, :stat => (stat && stat.to_hash))
         | 
| 123 191 | 
             
                  end
         | 
| 124 192 | 
             
                end
         | 
| 125 193 |  | 
| @@ -127,6 +195,7 @@ class ZookeeperBase | |
| 127 195 | 
             
                  include JZK::AsyncCallback::VoidCallback
         | 
| 128 196 |  | 
| 129 197 | 
             
                  def processResult(rc, path, queue)
         | 
| 198 | 
            +
                    logger.debug { "#{self.class.name}#processResult rc: #{rc}, req_id: #{req_id}, queue: #{queue.inspect}" }
         | 
| 130 199 | 
             
                    queue.push(:rc => rc, :req_id => req_id, :path => path)
         | 
| 131 200 | 
             
                  end
         | 
| 132 201 | 
             
                end
         | 
| @@ -140,7 +209,7 @@ class ZookeeperBase | |
| 140 209 | 
             
                  end
         | 
| 141 210 |  | 
| 142 211 | 
             
                  def process(event)
         | 
| 143 | 
            -
                    logger.debug { "WatcherCallback got event: #{event.to_hash}" }
         | 
| 212 | 
            +
                    logger.debug { "WatcherCallback got event: #{event.to_hash.inspect}" }
         | 
| 144 213 | 
             
                    hash = event.to_hash.merge(:req_id => req_id)
         | 
| 145 214 | 
             
                    @event_queue.push(hash)
         | 
| 146 215 | 
             
                  end
         | 
| @@ -156,26 +225,32 @@ class ZookeeperBase | |
| 156 225 | 
             
                  set_default_global_watcher(&watcher)
         | 
| 157 226 | 
             
                end
         | 
| 158 227 |  | 
| 159 | 
            -
                @ | 
| 228 | 
            +
                @start_stop_mutex.synchronize do
         | 
| 229 | 
            +
                  @jzk = JZK::ZooKeeper.new(@host, DEFAULT_SESSION_TIMEOUT, JavaCB::WatcherCallback.new(@event_queue))
         | 
| 160 230 |  | 
| 161 | 
            -
             | 
| 162 | 
            -
             | 
| 163 | 
            -
             | 
| 164 | 
            -
             | 
| 165 | 
            -
             | 
| 231 | 
            +
                  if timeout > 0
         | 
| 232 | 
            +
                    time_to_stop = Time.now + timeout
         | 
| 233 | 
            +
                    until connected?
         | 
| 234 | 
            +
                      break if Time.now > time_to_stop
         | 
| 235 | 
            +
                      sleep 0.1
         | 
| 236 | 
            +
                    end
         | 
| 166 237 | 
             
                  end
         | 
| 167 238 | 
             
                end
         | 
| 168 239 |  | 
| 169 240 | 
             
                state
         | 
| 170 241 | 
             
              end
         | 
| 171 242 |  | 
| 172 | 
            -
              def initialize(host, timeout=10, watcher=nil)
         | 
| 243 | 
            +
              def initialize(host, timeout=10, watcher=nil, options={})
         | 
| 173 244 | 
             
                @host = host
         | 
| 174 | 
            -
                @event_queue =  | 
| 245 | 
            +
                @event_queue = QueueWithPipe.new
         | 
| 175 246 | 
             
                @current_req_id = 0
         | 
| 176 247 | 
             
                @req_mutex = Monitor.new
         | 
| 177 248 | 
             
                @watcher_reqs = {}
         | 
| 178 249 | 
             
                @completion_reqs = {}
         | 
| 250 | 
            +
                @_running = nil
         | 
| 251 | 
            +
                @_closed  = false
         | 
| 252 | 
            +
                @options = {}
         | 
| 253 | 
            +
                @start_stop_mutex = Mutex.new
         | 
| 179 254 |  | 
| 180 255 | 
             
                watcher ||= get_default_global_watcher
         | 
| 181 256 |  | 
| @@ -184,6 +259,7 @@ class ZookeeperBase | |
| 184 259 |  | 
| 185 260 | 
             
                reopen(timeout, watcher)
         | 
| 186 261 | 
             
                return nil unless connected?
         | 
| 262 | 
            +
                @_running = true
         | 
| 187 263 | 
             
                setup_dispatch_thread!
         | 
| 188 264 | 
             
              end
         | 
| 189 265 |  | 
| @@ -203,6 +279,22 @@ class ZookeeperBase | |
| 203 279 | 
             
                state == JZK::ZooKeeper::States::ASSOCIATING
         | 
| 204 280 | 
             
              end
         | 
| 205 281 |  | 
| 282 | 
            +
              def running?
         | 
| 283 | 
            +
                @_running
         | 
| 284 | 
            +
              end
         | 
| 285 | 
            +
             | 
| 286 | 
            +
              def closed?
         | 
| 287 | 
            +
                @_closed
         | 
| 288 | 
            +
              end
         | 
| 289 | 
            +
             | 
| 290 | 
            +
              def self.set_debug_level(*a)
         | 
| 291 | 
            +
                # IGNORED IN JRUBY
         | 
| 292 | 
            +
              end
         | 
| 293 | 
            +
             | 
| 294 | 
            +
              def set_debug_level(*a)
         | 
| 295 | 
            +
                # IGNORED IN JRUBY
         | 
| 296 | 
            +
              end
         | 
| 297 | 
            +
             | 
| 206 298 | 
             
              def get(req_id, path, callback, watcher)
         | 
| 207 299 | 
             
                handle_keeper_exception do
         | 
| 208 300 | 
             
                  watch_cb = watcher ? create_watcher(req_id, path) : false
         | 
| @@ -332,20 +424,36 @@ class ZookeeperBase | |
| 332 424 |  | 
| 333 425 | 
             
              class DispatchShutdownException < StandardError; end
         | 
| 334 426 |  | 
| 427 | 
            +
              def wake_event_loop!
         | 
| 428 | 
            +
                @event_queue.push(KILL_TOKEN)    # ignored by dispatch_next_callback
         | 
| 429 | 
            +
              end
         | 
| 430 | 
            +
             | 
| 335 431 | 
             
              def close
         | 
| 336 432 | 
             
                @req_mutex.synchronize do
         | 
| 337 | 
            -
                  if @ | 
| 338 | 
            -
             | 
| 339 | 
            -
                     | 
| 340 | 
            -
             | 
| 433 | 
            +
                  @_running = false if @_running
         | 
| 434 | 
            +
                end
         | 
| 435 | 
            +
                    
         | 
| 436 | 
            +
                # XXX: why is wake_event_loop! here?
         | 
| 437 | 
            +
                if @dispatcher 
         | 
| 438 | 
            +
                  wake_event_loop!
         | 
| 439 | 
            +
                  @dispatcher.join 
         | 
| 440 | 
            +
                end
         | 
| 341 441 |  | 
| 342 | 
            -
             | 
| 343 | 
            -
             | 
| 344 | 
            -
                    @ | 
| 442 | 
            +
                unless @_closed
         | 
| 443 | 
            +
                  @start_stop_mutex.synchronize do
         | 
| 444 | 
            +
                    @_closed = true
         | 
| 445 | 
            +
                    close_handle
         | 
| 345 446 | 
             
                  end
         | 
| 447 | 
            +
             | 
| 448 | 
            +
                  @event_queue.close
         | 
| 346 449 | 
             
                end
         | 
| 450 | 
            +
              end
         | 
| 347 451 |  | 
| 348 | 
            -
             | 
| 452 | 
            +
              def close_handle
         | 
| 453 | 
            +
                if @jzk
         | 
| 454 | 
            +
                  @jzk.close
         | 
| 455 | 
            +
                  wait_until { !connected? }
         | 
| 456 | 
            +
                end
         | 
| 349 457 | 
             
              end
         | 
| 350 458 |  | 
| 351 459 | 
             
              # set the watcher object/proc that will receive all global events (such as session/state events)
         | 
| @@ -360,6 +468,23 @@ class ZookeeperBase | |
| 360 468 | 
             
                end
         | 
| 361 469 | 
             
              end
         | 
| 362 470 |  | 
| 471 | 
            +
              # by accessing this selectable_io you indicate that you intend to clear it
         | 
| 472 | 
            +
              # when you have delivered an event by reading one byte per event.
         | 
| 473 | 
            +
              #
         | 
| 474 | 
            +
              def selectable_io
         | 
| 475 | 
            +
                @event_queue.clear_reads_on_pop = false
         | 
| 476 | 
            +
                @event_queue.selectable_io
         | 
| 477 | 
            +
              end
         | 
| 478 | 
            +
             | 
| 479 | 
            +
              def get_next_event(blocking=true)
         | 
| 480 | 
            +
                @event_queue.pop(!blocking).tap do |event|
         | 
| 481 | 
            +
                  logger.debug { "get_next_event delivering event: #{event.inspect}" }
         | 
| 482 | 
            +
                  raise DispatchShutdownException if event == KILL_TOKEN
         | 
| 483 | 
            +
                end
         | 
| 484 | 
            +
              rescue ThreadError
         | 
| 485 | 
            +
                nil
         | 
| 486 | 
            +
              end
         | 
| 487 | 
            +
             
         | 
| 363 488 | 
             
              protected
         | 
| 364 489 | 
             
                def handle_keeper_exception
         | 
| 365 490 | 
             
                  yield
         | 
| @@ -376,28 +501,23 @@ class ZookeeperBase | |
| 376 501 | 
             
                end
         | 
| 377 502 |  | 
| 378 503 | 
             
                def create_watcher(req_id, path)
         | 
| 504 | 
            +
                  logger.debug { "creating watcher for req_id: #{req_id} path: #{path}" }
         | 
| 379 505 | 
             
                  lambda do |event|
         | 
| 506 | 
            +
                    logger.debug { "watcher for req_id #{req_id}, path: #{path} called back" }
         | 
| 380 507 | 
             
                    h = { :req_id => req_id, :type => event.type.int_value, :state => event.state.int_value, :path => path }
         | 
| 381 508 | 
             
                    @event_queue.push(h)
         | 
| 382 509 | 
             
                  end
         | 
| 383 510 | 
             
                end
         | 
| 384 511 |  | 
| 385 | 
            -
                def get_next_event
         | 
| 386 | 
            -
                  @event_queue.pop.tap do |event|
         | 
| 387 | 
            -
                    raise DispatchShutdownException if event == KILL_TOKEN
         | 
| 388 | 
            -
                  end
         | 
| 389 | 
            -
                end
         | 
| 390 | 
            -
                
         | 
| 391 512 | 
             
                # method to wait until block passed returns true or timeout (default is 10 seconds) is reached 
         | 
| 392 513 | 
             
                def wait_until(timeout=10, &block)
         | 
| 393 514 | 
             
                  time_to_stop = Time.now + timeout
         | 
| 394 515 | 
             
                  until yield do 
         | 
| 395 516 | 
             
                    break if Time.now > time_to_stop
         | 
| 396 | 
            -
                    sleep 0. | 
| 517 | 
            +
                    sleep 0.1
         | 
| 397 518 | 
             
                  end
         | 
| 398 519 | 
             
                end
         | 
| 399 520 |  | 
| 400 | 
            -
              protected
         | 
| 401 521 | 
             
                # TODO: Make all global puts configurable
         | 
| 402 522 | 
             
                def get_default_global_watcher
         | 
| 403 523 | 
             
                  Proc.new { |args|
         | 
| @@ -406,13 +526,10 @@ class ZookeeperBase | |
| 406 526 | 
             
                  }
         | 
| 407 527 | 
             
                end
         | 
| 408 528 |  | 
| 409 | 
            -
              private
         | 
| 410 529 | 
             
                def setup_dispatch_thread!
         | 
| 411 530 | 
             
                  logger.debug {  "starting dispatch thread" }
         | 
| 412 531 | 
             
                  @dispatcher = Thread.new do
         | 
| 413 | 
            -
                     | 
| 414 | 
            -
             | 
| 415 | 
            -
                    while Thread.current[:running]
         | 
| 532 | 
            +
                    while running?
         | 
| 416 533 | 
             
                      begin
         | 
| 417 534 | 
             
                        dispatch_next_callback 
         | 
| 418 535 | 
             
                      rescue DispatchShutdownException
         | 
    
        data/lib/zookeeper/common.rb
    CHANGED
    
    | @@ -4,18 +4,9 @@ module ZookeeperCommon | |
| 4 4 | 
             
              # sigh, i guess define this here?
         | 
| 5 5 | 
             
              ZKRB_GLOBAL_CB_REQ   = -1
         | 
| 6 6 |  | 
| 7 | 
            -
              def  | 
| 8 | 
            -
                 | 
| 9 | 
            -
             | 
| 10 | 
            -
             | 
| 11 | 
            -
              module ClassMethods
         | 
| 12 | 
            -
                def logger
         | 
| 13 | 
            -
                  @logger ||= Logger.new('/dev/null') # UNIX: YOU MUST USE IT!
         | 
| 14 | 
            -
                end
         | 
| 15 | 
            -
             | 
| 16 | 
            -
                def logger=(logger)
         | 
| 17 | 
            -
                  @logger = logger
         | 
| 18 | 
            -
                end
         | 
| 7 | 
            +
              def get_next_event(blocking=true)
         | 
| 8 | 
            +
                return nil if closed? # protect against this happening in a callback after close
         | 
| 9 | 
            +
                super(blocking) 
         | 
| 19 10 | 
             
              end
         | 
| 20 11 |  | 
| 21 12 | 
             
            protected
         | 
| @@ -50,13 +41,12 @@ protected | |
| 50 41 | 
             
                @req_mutex.synchronize { @completion_reqs.delete(req_id) }
         | 
| 51 42 | 
             
              end
         | 
| 52 43 |  | 
| 44 | 
            +
              def dispatch_next_callback(blocking=true)
         | 
| 45 | 
            +
                hash = get_next_event(blocking)
         | 
| 46 | 
            +
            #     Zookeeper.logger.debug { "get_next_event returned: #{hash.inspect}" }
         | 
| 53 47 |  | 
| 54 | 
            -
              def dispatch_next_callback
         | 
| 55 | 
            -
                hash = get_next_event
         | 
| 56 48 | 
             
                return nil unless hash
         | 
| 57 49 |  | 
| 58 | 
            -
                logger.debug {  "dispatch_next_callback got event: #{hash.inspect}" }
         | 
| 59 | 
            -
             | 
| 60 50 | 
             
                is_completion = hash.has_key?(:rc)
         | 
| 61 51 |  | 
| 62 52 | 
             
                hash[:stat] = ZookeeperStat::Stat.new(hash[:stat]) if hash.has_key?(:stat)
         | 
| @@ -81,9 +71,9 @@ protected | |
| 81 71 | 
             
                else
         | 
| 82 72 | 
             
                  logger.warn { "Duplicate event received (no handler for req_id #{hash[:req_id]}, event: #{hash.inspect}" }
         | 
| 83 73 | 
             
                end
         | 
| 74 | 
            +
                true
         | 
| 84 75 | 
             
              end
         | 
| 85 76 |  | 
| 86 | 
            -
             | 
| 87 77 | 
             
              def assert_supported_keys(args, supported)
         | 
| 88 78 | 
             
                unless (args.keys - supported).empty?
         | 
| 89 79 | 
             
                  raise ZookeeperExceptions::ZookeeperException::BadArguments,  # this heirarchy is kind of retarded
         | 
| @@ -98,9 +88,5 @@ protected | |
| 98 88 | 
             
                end
         | 
| 99 89 | 
             
              end
         | 
| 100 90 |  | 
| 101 | 
            -
              # supplied by parent class impl.
         | 
| 102 | 
            -
              def logger
         | 
| 103 | 
            -
                self.class.logger
         | 
| 104 | 
            -
              end
         | 
| 105 91 | 
             
            end
         | 
| 106 92 |  | 
| @@ -0,0 +1,135 @@ | |
| 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 | 
            +
                  super(*a, &b)
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                # EM::DefaultDeferrable that will be called back when our em_connection has been detached
         | 
| 18 | 
            +
                # and we've completed the close operation
         | 
| 19 | 
            +
                def on_close(&block)
         | 
| 20 | 
            +
                  @on_close.callback(&block) if block
         | 
| 21 | 
            +
                  @on_close
         | 
| 22 | 
            +
                end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                # called after we've successfully registered our selectable_io to be
         | 
| 25 | 
            +
                # managed by the EM reactor
         | 
| 26 | 
            +
                def on_attached(&block)
         | 
| 27 | 
            +
                  @on_attached.callback(&block) if block
         | 
| 28 | 
            +
                  @on_attached
         | 
| 29 | 
            +
                end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                # returns a Deferrable that will be called when the Zookeeper C event loop
         | 
| 32 | 
            +
                # has been shut down 
         | 
| 33 | 
            +
                #
         | 
| 34 | 
            +
                # if a block is given, it will be registered as a callback when the
         | 
| 35 | 
            +
                # connection has been closed
         | 
| 36 | 
            +
                #
         | 
| 37 | 
            +
                def close(&block)
         | 
| 38 | 
            +
                  on_close(&block)
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                    logger.debug { "close called, closed? #{closed?} running? #{running?}" }
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                  if @_running
         | 
| 43 | 
            +
                    @start_stop_mutex.synchronize do
         | 
| 44 | 
            +
                      @_running = false
         | 
| 45 | 
            +
                    end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                    @em_connection.detach if @em_connection
         | 
| 48 | 
            +
                    @em_connection = nil
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                    unless @_closed
         | 
| 51 | 
            +
                      @start_stop_mutex.synchronize do
         | 
| 52 | 
            +
                        logger.debug { "closing handle" }
         | 
| 53 | 
            +
                        close_handle
         | 
| 54 | 
            +
                      end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                      selectable_io.close unless selectable_io.closed?
         | 
| 57 | 
            +
                    end
         | 
| 58 | 
            +
                  else
         | 
| 59 | 
            +
                    logger.debug { "we are not running, so returning on_close deferred" }
         | 
| 60 | 
            +
                  end
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                  on_close.succeed
         | 
| 63 | 
            +
                  on_close
         | 
| 64 | 
            +
                end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                # make this public as the ZKConnection object needs to call it
         | 
| 67 | 
            +
                public :dispatch_next_callback
         | 
| 68 | 
            +
             | 
| 69 | 
            +
              protected
         | 
| 70 | 
            +
                # instead of setting up a dispatch thread here, we instead attach
         | 
| 71 | 
            +
                # the #selectable_io to the event loop 
         | 
| 72 | 
            +
                def setup_dispatch_thread!
         | 
| 73 | 
            +
                  EM.schedule do
         | 
| 74 | 
            +
                    if running? and not closed?
         | 
| 75 | 
            +
                      begin
         | 
| 76 | 
            +
                        @em_connection = EM.watch(selectable_io, ZKConnection, self) { |cnx| cnx.notify_readable = true }
         | 
| 77 | 
            +
                      rescue Exception => e
         | 
| 78 | 
            +
                        $stderr.puts "caught exception from EM.watch(): #{e.inspect}"
         | 
| 79 | 
            +
                      end
         | 
| 80 | 
            +
                      on_attached.succeed
         | 
| 81 | 
            +
                    end
         | 
| 82 | 
            +
                  end
         | 
| 83 | 
            +
                end
         | 
| 84 | 
            +
              end
         | 
| 85 | 
            +
             | 
| 86 | 
            +
              # this class is handed to EventMachine.watch to handle event dispatching
         | 
| 87 | 
            +
              # when the queue has a message waiting. There's a pipe shared between 
         | 
| 88 | 
            +
              # the event thread managed by the queue implementation in C. It's made
         | 
| 89 | 
            +
              # available to the ruby-space through the Zookeeper#selectable_io method.
         | 
| 90 | 
            +
              # When the pipe is readable, that means there's an event waiting. We call
         | 
| 91 | 
            +
              # dispatch_next_event and read a single byte off the pipe.
         | 
| 92 | 
            +
              #
         | 
| 93 | 
            +
              class ZKConnection < EM::Connection
         | 
| 94 | 
            +
             | 
| 95 | 
            +
                def initialize(zk_client)
         | 
| 96 | 
            +
                  @zk_client = zk_client
         | 
| 97 | 
            +
                  @attached = true
         | 
| 98 | 
            +
                end
         | 
| 99 | 
            +
             | 
| 100 | 
            +
                def attached?
         | 
| 101 | 
            +
                  @attached
         | 
| 102 | 
            +
                end
         | 
| 103 | 
            +
             | 
| 104 | 
            +
                def detach
         | 
| 105 | 
            +
                  return unless @attached
         | 
| 106 | 
            +
                  @attached = false
         | 
| 107 | 
            +
                  super
         | 
| 108 | 
            +
                  logger.debug { "#{self.class.name}: detached" }
         | 
| 109 | 
            +
                end
         | 
| 110 | 
            +
             | 
| 111 | 
            +
                # we have an event waiting
         | 
| 112 | 
            +
                def notify_readable
         | 
| 113 | 
            +
                  if @zk_client.running?
         | 
| 114 | 
            +
            #         logger.debug { "#{self.class.name}: dispatching events while #{@zk_client.running?}" }
         | 
| 115 | 
            +
             | 
| 116 | 
            +
                    read_io_nb if @zk_client.dispatch_next_callback(false)
         | 
| 117 | 
            +
             | 
| 118 | 
            +
                  elsif attached?
         | 
| 119 | 
            +
                    logger.debug { "#{self.class.name}: @zk_client was not running? and attached? #{attached?}, detaching!" }
         | 
| 120 | 
            +
                    detach
         | 
| 121 | 
            +
                  end
         | 
| 122 | 
            +
                end
         | 
| 123 | 
            +
             | 
| 124 | 
            +
                private
         | 
| 125 | 
            +
                  def read_io_nb(size=1)
         | 
| 126 | 
            +
                    @io.read_nonblock(1)
         | 
| 127 | 
            +
                  rescue Errno::EWOULDBLOCK, Errno::EAGAIN, IOError
         | 
| 128 | 
            +
                  end
         | 
| 129 | 
            +
                  
         | 
| 130 | 
            +
                  def logger
         | 
| 131 | 
            +
                    Zookeeper.logger
         | 
| 132 | 
            +
                  end
         | 
| 133 | 
            +
              end
         | 
| 134 | 
            +
            end
         | 
| 135 | 
            +
             | 
    
        data/lib/zookeeper.rb
    CHANGED
    
    | @@ -19,6 +19,18 @@ end | |
| 19 19 | 
             
            require 'zookeeper_base'
         | 
| 20 20 |  | 
| 21 21 | 
             
            class Zookeeper < ZookeeperBase
         | 
| 22 | 
            +
              unless defined?(@@logger)
         | 
| 23 | 
            +
                @@logger = Logger.new('/dev/null').tap { |l| l.level = Logger::FATAL } # UNIX: FOR GREAT JUSTICE !!
         | 
| 24 | 
            +
              end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
              def self.logger
         | 
| 27 | 
            +
                @@logger
         | 
| 28 | 
            +
              end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
              def self.logger=(logger)
         | 
| 31 | 
            +
                @@logger = logger
         | 
| 32 | 
            +
              end
         | 
| 33 | 
            +
             | 
| 22 34 | 
             
              def reopen(timeout=10, watcher=nil)
         | 
| 23 35 | 
             
                super
         | 
| 24 36 | 
             
              end
         | 
| @@ -130,6 +142,11 @@ class Zookeeper < ZookeeperBase | |
| 130 142 | 
             
                options[:callback] ? rv : rv.merge(:acl => acls, :stat => Stat.new(stat))
         | 
| 131 143 | 
             
              end
         | 
| 132 144 |  | 
| 145 | 
            +
              # close this client and any underyling connections
         | 
| 146 | 
            +
              def close
         | 
| 147 | 
            +
                super
         | 
| 148 | 
            +
              end
         | 
| 149 | 
            +
             | 
| 133 150 | 
             
              def state
         | 
| 134 151 | 
             
                super
         | 
| 135 152 | 
             
              end
         | 
| @@ -146,6 +163,61 @@ class Zookeeper < ZookeeperBase | |
| 146 163 | 
             
                super
         | 
| 147 164 | 
             
              end
         | 
| 148 165 |  | 
| 166 | 
            +
              # for expert use only. set the underlying debug level for the C layer, has no
         | 
| 167 | 
            +
              # effect in java
         | 
| 168 | 
            +
              #
         | 
| 169 | 
            +
              def self.set_debug_level(val)
         | 
| 170 | 
            +
                super
         | 
| 171 | 
            +
              end
         | 
| 172 | 
            +
             | 
| 173 | 
            +
              # DEPRECATED: use the class-level method instead
         | 
| 174 | 
            +
              def set_debug_level(val)
         | 
| 175 | 
            +
                super
         | 
| 176 | 
            +
              end
         | 
| 177 | 
            +
             | 
| 178 | 
            +
              # has the underlying connection been closed?
         | 
| 179 | 
            +
              def closed?
         | 
| 180 | 
            +
                super
         | 
| 181 | 
            +
              end
         | 
| 182 | 
            +
             | 
| 183 | 
            +
              # is the event delivery system running?
         | 
| 184 | 
            +
              def running?
         | 
| 185 | 
            +
                super
         | 
| 186 | 
            +
              end
         | 
| 187 | 
            +
             | 
| 188 | 
            +
              # returns an IO object that will be readable when an event is ready for dispatching
         | 
| 189 | 
            +
              # (for internal use only)
         | 
| 190 | 
            +
              def selectable_io
         | 
| 191 | 
            +
                super
         | 
| 192 | 
            +
              end
         | 
| 193 | 
            +
             | 
| 194 | 
            +
              # closes the underlying connection object
         | 
| 195 | 
            +
              # (for internal use only)
         | 
| 196 | 
            +
              def close_handle
         | 
| 197 | 
            +
                super
         | 
| 198 | 
            +
              end
         | 
| 199 | 
            +
             | 
| 200 | 
            +
            protected
         | 
| 201 | 
            +
              # used during shutdown, awaken the event delivery thread if it's blocked
         | 
| 202 | 
            +
              # waiting for the next event
         | 
| 203 | 
            +
              def wake_event_loop!
         | 
| 204 | 
            +
                super
         | 
| 205 | 
            +
              end
         | 
| 206 | 
            +
              
         | 
| 207 | 
            +
              # starts the event delivery subsystem going. after calling this method, running? will be true
         | 
| 208 | 
            +
              def setup_dispatch_thread!
         | 
| 209 | 
            +
                super
         | 
| 210 | 
            +
              end
         | 
| 211 | 
            +
             | 
| 212 | 
            +
              # TODO: describe what this does
         | 
| 213 | 
            +
              def get_default_global_watcher
         | 
| 214 | 
            +
                super
         | 
| 215 | 
            +
              end
         | 
| 216 | 
            +
             | 
| 217 | 
            +
              def logger
         | 
| 218 | 
            +
                Zookeeper.logger
         | 
| 219 | 
            +
              end
         | 
| 220 | 
            +
             | 
| 149 221 | 
             
            private
         | 
| 150 222 | 
             
              def setup_call(opts)
         | 
| 151 223 | 
             
                req_id = nil
         | 
| @@ -175,6 +247,5 @@ private | |
| 175 247 | 
             
              def assert_open
         | 
| 176 248 | 
             
                super
         | 
| 177 249 | 
             
              end
         | 
| 178 | 
            -
             | 
| 179 250 | 
             
            end
         | 
| 180 251 |  | 
    
        data/slyphon-zookeeper.gemspec
    CHANGED
    
    | @@ -3,7 +3,7 @@ $:.push File.expand_path("../lib", __FILE__) | |
| 3 3 |  | 
| 4 4 | 
             
            Gem::Specification.new do |s|
         | 
| 5 5 | 
             
              s.name        = "slyphon-zookeeper"
         | 
| 6 | 
            -
              s.version     = '0. | 
| 6 | 
            +
              s.version     = '0.2.0'
         | 
| 7 7 |  | 
| 8 8 | 
             
              s.authors     = ["Phillip Pearson", "Eric Maland", "Evan Weaver", "Brian Wickman", "Neil Conway", "Jonathan D. Simms"]
         | 
| 9 9 | 
             
              s.email       = ["slyphon@gmail.com"]
         | 
| @@ -16,7 +16,7 @@ Gem::Specification.new do |s| | |
| 16 16 | 
             
              s.files         = `git ls-files`.split("\n")
         | 
| 17 17 | 
             
              s.require_paths = ["lib"]
         | 
| 18 18 |  | 
| 19 | 
            -
              if ENV['JAVA_GEM']
         | 
| 19 | 
            +
              if ENV['JAVA_GEM'] or defined?(::JRUBY_VERSION)
         | 
| 20 20 | 
             
                s.platform = 'java'
         | 
| 21 21 | 
             
                s.add_runtime_dependency('slyphon-log4j',         '= 1.2.15')
         | 
| 22 22 | 
             
                s.add_runtime_dependency('slyphon-zookeeper_jar', '= 3.3.3')
         |