Maher4Ever / wdm

Windows Directory Monitor (WDM) is a threaded directories monitor for Windows.
https://rubygems.org/gems/wdm
MIT License
50 stars 14 forks source link

JRuby compatible version? #8

Open codylerum opened 11 years ago

codylerum commented 11 years ago

Since a lot of windows users are using JRuby to work around/through other compatibility issues, what would be the issues involved with creating a java compatible gem?

thibaudgg commented 11 years ago

On jruby I got this warning message on install:

gem install wdm                                                                                                                                       Fetching: wdm-0.1.0.gem (100%)
Building native extensions.  This could take a while...
ERROR:  Error installing wdm:
    ERROR: Failed to build gem native extension.

        /Users/Thibaud/.rbenv/versions/jruby-1.7.3/bin/jruby extconf.rb
NotImplementedError: C extension support is not enabled. Pass -Xcext.enabled=true to JRuby or set JRUBY_OPTS or modify .jrubyrc to enable.

   (root) at /Users/Thibaud/.rbenv/versions/jruby-1.7.3/lib/ruby/shared/mkmf.rb:8
  require at org/jruby/RubyKernel.java:1027
   (root) at /Users/Thibaud/.rbenv/versions/jruby-1.7.3/lib/ruby/shared/rubygems/custom_require.rb:1
   (root) at extconf.rb:1

Gem files will remain installed in /Users/Thibaud/.rbenv/versions/jruby-1.7.3/lib/ruby/gems/shared/gems/wdm-0.1.0 for inspection.
Results logged to /Users/Thibaud/.rbenv/versions/jruby-1.7.3/lib/ruby/gems/shared/gems/wdm-0.1.0/ext/wdm/gem_make.out

Even if it doesn't work yet on jruby, it would be great if the gem installation was possible for Listen users. More info: https://github.com/guard/listen/pull/97

Thanks @Maher4Ever !

netzpirat commented 11 years ago

Just played a bit with the Java 7 WatchService and it's not to hard to make use of the file modification detection on Jruby:

require 'java'

fs = java.nio.file.FileSystems.default
watcher = fs.new_watch_service

path = fs.getPath '/tmp'
path.register(watcher,
           java.nio.file.StandardWatchEventKinds::ENTRY_CREATE,
           java.nio.file.StandardWatchEventKinds::ENTRY_DELETE,
           java.nio.file.StandardWatchEventKinds::ENTRY_MODIFY)

puts 'Listening'

while true do
  key = watcher.take

  key.poll_events.each do |event|
    next if event.kind == java.nio.file.StandardWatchEventKinds::OVERFLOW
    file = event.context.to_absolute_path.to_file
    puts event.kind, file
  end

  key.reset
end    

However there's a huge delay (1-3 seconds) on OS X until the event is received, so you might want to try it on Windows to see if there's less delay. If you're interested, I started a JVM adapter for Listen, but I've quickly loosed interest since I don't use Windows and also not JRuby for development, but perhaps you might want to pick it up:

diff --git a/Gemfile b/Gemfile
index 7a077ba..0ec8dac 100644
--- a/Gemfile
+++ b/Gemfile
@@ -13,7 +13,7 @@ gem 'wdm',        '~> 0.0.3' if RbConfig::CONFIG['target_os'] =~ /mswin|mingw/i
 group :development do
   gem 'guard-rspec'
   gem 'yard'
-  gem 'redcarpet'
+  #gem 'redcarpet'
   gem 'pimpmychangelog'
 end

diff --git a/lib/listen/adapter.rb b/lib/listen/adapter.rb
index a904093..c539b57 100644
--- a/lib/listen/adapter.rb
+++ b/lib/listen/adapter.rb
@@ -9,7 +9,7 @@ module Listen
                   :mutex, :changed_directories, :turnstile, :latency

     # The list of existing optimized adapters.
-    OPTIMIZED_ADAPTERS = %w[Darwin Linux BSD Windows]
+    OPTIMIZED_ADAPTERS = %w[JVM Darwin Linux BSD Windows]

     # The list of existing fallback adapters.
     FALLBACK_ADAPTERS = %w[Polling]
diff --git a/lib/listen/adapters/jvm.rb b/lib/listen/adapters/jvm.rb
index e69de29..b27f006 100644
--- a/lib/listen/adapters/jvm.rb
+++ b/lib/listen/adapters/jvm.rb
@@ -0,0 +1,97 @@
+module Listen
+  module Adapters
+
+    # Listener implementation for the Java Virtual Machine
+    #
+    class JVM < Adapter
+
+      attr_accessor :worker, :worker_thread, :poll_thread
+
+      # Initializes the Adapter.
+      #
+      # @see Listen::Adapter#initialize
+      #
+      def initialize(directories, options = {}, &callback)
+        super
+        @worker = init_worker
+      end
+
+      # Starts the adapter.
+      #
+      # @param [Boolean] blocking whether or not to block the current thread after starting
+      #
+      def start(blocking = true)
+        super
+
+        @worker_thread = Thread.new do
+          until stopped
+            key = worker.take
+
+            key.poll_events.each do |event|
+              next if paused || event.kind == java.nio.file.StandardWatchEventKinds::OVERFLOW
+
+              file = event.context.to_absolute_path.to_file
+
+              mutex.synchronize do
+                @changed_directories << (file.directory? ? file : file.parent_file)
+              end
+            end
+
+            key.reset
+          end
+        end
+
+        @poll_thread = Thread.new { poll_changed_directories } if report_changes?
+        worker_thread.join if blocking
+      end
+
+      # Stops the adapter.
+      #
+      def stop
+        mutex.synchronize do
+          return if stopped
+          super
+        end
+
+        worker.close
+        Thread.kill(worker_thread) if worker_thread
+        poll_thread.join if poll_thread
+      end
+
+      # Checks if the adapter is running on the JVM.
+      #
+      # @return [Boolean] whether usable or not
+      #
+      def self.usable?
+        return true if defined? JRUBY_VERSION
+      end
+
+      private
+
+      # Initializes a WatchService and adds a WatchKey for each files in
+      # the directories passed to the adapter.
+      #
+      # @return [WatchService] initialized watch service
+      #
+      def init_worker
+        require 'java'
+
+        fs      = java.nio.file.FileSystems.default
+        watcher = fs.new_watch_service
+
+        directories.each do |dir|
+          path = fs.getPath dir
+          path.register(watcher,
+                       java.nio.file.StandardWatchEventKinds::ENTRY_CREATE,
+                       java.nio.file.StandardWatchEventKinds::ENTRY_DELETE,
+                       java.nio.file.StandardWatchEventKinds::ENTRY_MODIFY)
+
+        end
+
+        watcher
+      end
+
+    end
+
+  end
+end
diff --git a/spec/listen/adapters/jvm_spec.rb b/spec/listen/adapters/jvm_spec.rb
index e69de29..f6bd398 100644
--- a/spec/listen/adapters/jvm_spec.rb
+++ b/spec/listen/adapters/jvm_spec.rb
@@ -0,0 +1,25 @@
+require 'spec_helper'
+
+describe Listen::Adapters::JVM do
+  if jruby?
+    if Listen::Adapters::JVM.usable?
+      it 'is usable on the JVM' do
+        described_class.should be_usable
+      end
+
+      it_should_behave_like 'a filesystem adapter'
+      it_should_behave_like 'an adapter that call properly listener#on_change'
+    else
+      it "isn't usable on BSD with #{RbConfig::CONFIG['RUBY_INSTALL_NAME']}" do
+        described_class.should_not be_usable
+      end
+    end
+  end
+
+  unless jruby?
+    it "isn't usable without the JVM" do
+      described_class.should_not be_usable
+    end
+  end
+
+end
diff --git a/spec/support/platform_helper.rb b/spec/support/platform_helper.rb
index 480b21f..c5af12e 100644
--- a/spec/support/platform_helper.rb
+++ b/spec/support/platform_helper.rb
@@ -13,3 +13,7 @@ end
 def windows?
   RbConfig::CONFIG['target_os'] =~ /mswin|mingw/i
 end
+
+def jruby?
+  defined? JRUBY_VERSION
+end
thibaudgg commented 11 years ago

Nice!

perlun commented 11 years ago

Very good. We should definitely try to get something along these lines working (either in wdm, in the listen gem or in a separate JRuby-specific notifier), since Listen is depending on polling on JRuby/Windows at the moment (with a very noticable delay, much longer than 1-3 seconds).

mechanicalduck commented 9 years ago

Will / has this been merged to master? I use guard/listen on Windows, too and also using JRuby to work around compatibility issues, it recommends wdm as it would just simply poll otherwise, which is very slow and inefficient.

thibaudgg commented 9 years ago

@mechanicalduck I really not sure @Maher4Ever hasn't be active for a long time now and @netzpirat past away a few month ago, maybe the best would be to fork this repo.