(перевод http://blogs.sun.com/coolstuff/entry/using_java_classes_in_jruby)
Подключение (require) JAR файлов
Во-первых, конечно же, необходимо подключить модуль, который наведет мост между JRuby и JVM:
include Java
Примечание:
- Написание с большой буквы имеет значение.
- А вот такой вариант выдает ошибку, мол требуется имя модуля, а вместо этого предлагается строка:
include 'java'
- А такой вариант все еще присутствует на нескольких сайтах. Он не выдает явных ошибок, но тем не менее у меня не работает:
require '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 файлы.
Варианты:
- Поместить их в CLASSPATH (у меня не заработало. Такое впечатление, что JRuby не читает переменные среды окружения)
- Поместить их в $RUBY_HOME/lib (не пытался сделать это)
- В NetBeans для windows, поместить в JRUBY_EXTRA_CLASSPATH (так же не заработало у меня)
- Сдаться наконец и указать jar files с полным путем, как я сделал в Step #2.
- Последовать примеру 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/