Martian eyrie

(перевод http://blogs.sun.com/coolstuff/entry/using_java_classes_in_jruby)

Подключение (require) JAR файлов 

 Во-первых, конечно же, необходимо подключить модуль, который наведет мост между JRuby и JVM:

include Java

Примечание:

Примечание:
Кавычки являются ошибкой в этом include выражении и также в include_class выражении. Но они требуются при использовании require. Мнемоник для запоминания "Quotes are required, not included."

Затем требуется подключить посредством require каждого jar файла, которые используются программой хотя бы раз, даже если на них и нет прямой ссылки в JRuby скрипте.

Многие из jar файлов которые я прикрутил с помощью require были неявными. Например, если есть код:
   x = someMethod().someOtherMethod.aThirdMethod()
в таком случае каждый промежуточный класс должен быть добавлен require в код. (Т.к. я конвертил Groovy скрипт, я часто не знал чем был тот или иной класс). Но даже когда объекты обращаются к классам внутренне, эти классы должны быть добавлены require в программу так, что JVM знала где их найти.

Таким образом, я запускал программу, получал ошибку об отсутствующем классе, искал его в 30-40 jar файлах, участвующих в CMS, и затем добавлял новое require выражение когда его находил. (В конце концов я написал скрипт для поиска jar файла и это было поучительно. API документы рассказали мне в каком пакете находился класс, но было бы здорово, если там же были ссылки и на JAR, в котором он содержится.)

Примечание:
Было бы круто, если можно использовать знаки подстановки (*) в CLASSPATH настройке. Но или хотя бы хотя бы require выражения позволяли бы их использовать:
   require "/some/path/*.jar"
Что-то типа этого спасло бы меня от многих проблем!

Использование имен классов и ссылки на них

В большинстве случаев, подключение jar файла это все что нужно сделать. JRuby обычно может определить тип, так что вы не должны явно включать эти классы. Но бывают случаи, когда нужно указать имя класса в коде (например, доступ к статичному методу). В этих случаях необходимо использовать выражение include_class в вашем коде.

Когда подключаете классы, и когда обращаетесь к классу в коде, имена пакетов, которые начинаются на java, javax, org, и com "особенные". Вы можете использовать эти префиксы пакетов как любую другую переменную. Другие же ссылки на пакеты в коде нужно указывать с префиксом Java::  

Note:
Эти префиксы "особенные" как по отношению к внешним пакетам, так и к ядерным JVM классам. (Если вы забыли указать префикс, JRuby выдаст ошибку "invalid method", потому что считает, что первая часть паекта – метод, который возвращает объект)

Можно также использовать константы для задания пути:

JFrame = javax.swing.JFrame

...

_frame = JFrame.new()

 

Доступ к внешним классам 

Пока все хорошо. Но почему-то я не смог найти достаточно информации как получить доступ к 3rd party классам. Исследовав ресурсы и сделав множество экспериментов, я собрал способы сделать это следующим образом:

1. Указать JRuby где находится JAR файлы.
    Варианты:

  1. Поместить их в CLASSPATH (у меня не заработало. Такое впечатление, что JRuby не читает переменные среды окружения)
  2. Поместить их в $RUBY_HOME/lib (не пытался сделать это) 
  3. В NetBeans для windows, поместить в JRUBY_EXTRA_CLASSPATH (так же не заработало у меня)
  4. Сдаться наконец и указать jar files с полным путем, как я сделал в Step #2.
  5. Последовать примеру Rob Di Marcoи поместить опцию -I<directory> в командной строке для каждой директории, содержащей jar файл.

AND
2. Require каждый jar по имени, указав полный путь, если директория, в которой он находится, не указана в командной строке, как в Step #1.

    К моему удивлению, настройка CLASSPATH в среде окружения не подхватывается, когда JRuby скрипт выполняется на Solaris. Поэтому рабочий вариант такой:
         require "/some/path/MyStuff.jar"
    А не такой:
         require "MyStuff.jar"

    Без -I опций в командной строке, последний вариант выдает ошибку "no such file to load".

P.S. (как вариант в комментариях был предложен
Dir["/some/path/*.jar"].each { |jar| require jar }  )

AND
3. Примените
include_class на каждый класс, к которому нужно будет обращаться по имени в коде, используйте полное указание имени пакета. (Укажите Java:: префикс, если имя пакета не входит в число “особенных” см.выше.)

   Итак, если статический метод возвращает объект Foo, код будет выглядеть следующим образом:
   include_class Java::some.package.MyClass
   ...
   x = MyClass.staticGetMethod()

(Note that Foo does not need an include_class statement, since it is not explicitly named in the code.) 

Реализация Java интерфейса

Согласно странице на Nabble,
http://www.nabble.com/What-is-the-current-syntax-for-defining-a-class-in-JRuby-that-implements-a-Java-interface--t4277539.html

Подключение интерфейсов в класс происходит, как если бы они были модулями:

class SomeClass

  include java.lang.Runnable

  include java.lang.Comparable

  def run

    ...

  end

  ...

Или же можно сгруппировать их в один модуль и подключить его:

 

module RunCompare
 
    include java.lang.Runnable
 
    include java.lang.Comparable
 
  end

class SomeClass
 
    include RunCompare
 
    def run
 
      ...
 
    end
 
    ...

Реализация же интерфейса выглядит следующим образом:

require 'java'

class Foo
include java.lang.Runnable
def run
# Runnable stuff hear
end
end

# To Run
java.lang.Thread.new(Foo.new).start

 

Источники

Перевод статьи
http://blogs.sun.com/coolstuff/entry/using_java_classes_in_jruby

Headius:
http://www.headius.com/jrubywiki/index.php/Calling_Java_from_JRuby

Sun:
http://java.sun.com/developer/technicalArticles/scripting/jruby/

Nabble: http://www.nabble.com/What-is-the-current-syntax-for-defining-a-class-in-JRuby-that-implements-a-Java-interface--t4277539.html

Rob Di Marco's post at Innovation on the Run:
http://www.innovationontherun.com/scraping-dynamic-websites-using-jruby-and-htmlunit/