JRuby compatible version? #8

Open
codylerum opened this Issue Mar 27, 2013 · 6 comments

Projects

None yet

5 participants

@codylerum

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

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: guard/listen#97

Thanks @Maher4Ever !

@netzpirat

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

Nice!

@perlun

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

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

@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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment