Calling Java from JRuby
From JRubyWiki
Contents |
Basics: 'include Java' and Accessing Java Classes and Methods
As of JRuby 1.0, a special 'include Java' directive in your file will give you access to the bundled Java libraries. However, this will not give you access to non-bundled libraries; a bit more is needed for that, which will be discussed later.
All of the following examples can be tested using jirb_swing the Swing-based IRB console that comes with JRuby.
The following code shows this. unless I've messed it up while adding wiki tags, it should pop up a small window showing "Hello" on your screen.
# Valid as of JRuby 1.0
# This is the 'magical Java include line'.
include Java
# With the 'include Java' above, we can now refer to things that are part of the
# standard Java platform via their full paths.
frame = javax.swing.JFrame.new("Window") # Creating a Java JFrame.
label = javax.swing.JLabel.new("Hello")
# We can transparently call Java methods on Java objects, just as if they were defined in Ruby.
frame.getContentPane.add(label) # Invoking the Java method 'getContentPane'.
frame.setDefaultCloseOperation(javax.swing.JFrame::EXIT_ON_CLOSE)
frame.pack
frame.setVisible(true)
Note: if you are testing the example above in the Swing IRB console jirb_swing, change the default close operation to DISPOSE_ON_CLOSE, or HIDE_ON_CLOSE unless you want jirb_swing to also close when you close the second window.
Here's another example (showing results from testing these statements in the jirb console).
Let's say you wanted to get a list of network interfaces. Here's the Java api docs for java.net.NetworkInterface.
Here's how to access the methods from this Java Class from from JRuby:
irb(main):013:0> ni = java.net.NetworkInterface.networkInterfaces => #<#<Class:01x7e666f>:0x855a27 @java_object=java.net.NetworkInterface$1@821453>
ni is Ruby variable holding a Java Enumeration of NetworkInterfaces. You can see the Class ancestry for ni like this:
irb(main):029:0> ni.class.ancestors => [#<Class:01x7e666f>, Java::JavaUtil::Enumeration, Enumerable, Java::JavaLang::Object, ConcreteJavaProxy, JavaProxy, JavaProxyMethods, Object, Java, Kernel]
Enumeration elements can't be accessed using Array#[] syntax but they do appear as Arrays for many other purposes. You can find out both the Java and Ruby methods for an Enumeration of NetworkInterfaces like this:
irb(main):032:0> java.net.NetworkInterface.networkInterfaces.methods => ["__jsend!", "has_more_elements", "hasMoreElements", "next_element", "nextElement", "each", "reject", "member?", "grep", "include?", "min", "sort", "any?", "partition", "each_with_index", "collect", "find_all", "to_a", "inject", "detect", "map", "zip", "sort_by", "max", "entries", "all?", "find", "select", "hashCode", "notifyAll", "getClass", "to_string", "toString", "get_class", "notify_all", "equals", "hash_code", "wait", "notify", "__jcreate!", "java_class", "eql?", "synchronized", "to_java_object", "equal?", "java_object", "java_object=", "to_s", "==", "hash", "java_kind_of?", "handle_different_imports", "include_class", "display", "object_id", "frozen?", "org", "__id__", "clone", "__send__", "id", "__jtrap", "instance_eval", "singleton_methods", "is_a?", "extend", "instance_variable_set", "freeze", "remove_instance_variable", "=~", "private_methods", "methods", "instance_variable_get", "nil?", "send", "untaint", "com", "type", "class", "===", "instance_of?", "protected_methods", "tainted?", "kind_of?", "javax", "inspect", "java", "instance_exec", "taint", "dup", "public_methods", "instance_variable_defined?", "respond_to?", "method", "instance_variables"]
Because JRuby supports the #each method on Java Enumerations you can do this:
irb(main):011:0> java.net.NetworkInterface.networkInterfaces.each {|i| puts i; puts }
name:en1 (en1) index: 5 addresses:
/63.138.152.170;
/fe80:0:0:0:21b:63ff:febf:4a9d%5;
name:en0 (en0) index: 4 addresses:
/63.138.152.125;
/fe80:0:0:0:21b:63ff:fe1e:b2da%4;
name:lo0 (lo0) index: 1 addresses:
/127.0.0.1;
/fe80:0:0:0:0:0:0:1%1;
/0:0:0:0:0:0:0:1%0;
Require a jar file to make resources in the jar discoverable within JRuby
In order to use resources within a jar file from JRuby the jar file must either be on the classpath or you can make it available with the require method:
require 'path/to/mycode.jar'
Will make the resources in mycode.jar discoverable by commands like import and include_package.
Import a Java Class to use it within JRuby
The import statement can be used to import a Java Class.
Example: import and use the java.lang.System class.
include Java import java.lang.System version = System.getProperties["java.runtime.version"]
Use include_package within a Ruby Module to import a Java Package
Use include_package "<package_name>" in a Ruby Module to support namespaced access to the Java classes in the package.
Example: create a Ruby Module called JavaLang that includes the classes in the Java package java.lang.
module JavaLang include_package "java.lang" end
Prefix the Class name with JavaLang:: to access the included Classes:
version = JavaLang::System.getProperties["java.runtime.version"] => "1.5.0_13-b05-237"
processors = JavaLang::Runtime.getRuntime.availableProcessors => 2
The Java classes in the package will become available in this class/module, unless a constant with the same name as a Java class is already defined.
The use of the Module name to scope access to the imported Java class is also helpful in cases where the Java class has the same name as an existing Ruby class.
For example if you need to create an instance of a java.io.File object this code will work:
import java.io.File
newfile = File.new("file.txt")
=> #<Java::JavaIo::File:0xdc6f00 @java_object=file.txt>
However you've now redefined the Ruby constant File and can no longer access the Ruby File class. Executing this:
File.open('README', 'r') {|f| puts f.readline }
Will produce this error:
NoMethodError: private method `open' called for Java::JavaIo::File:Class
If instead you create a module called JavaIO and include the package in the module definition:
module JavaIO include_package "java.io" end
You can now create a new instance of the Java class File without shadowing the Ruby version of the File class.:
newfile = JavaIO::File.new("file.txt")
=> #<Java::JavaIo::File:0x15619c @java_object=file.txt>
The Ruby File class is still accessible:
File.open('README', 'r') {|f| puts f.readline }
JRuby - A Java implementation of the Ruby language
=> nil
Mapping of Java class in JRuby
Imported classes are mapped into Ruby name space as follows:
Java: org.foo.department.Widget Ruby: Java::OrgFooDepartment::Widget
Conversion of Types
Ruby Array to Java String
["a","b","c"].to_java(:string) => #<#<Class:01x51c603>:0x9ef43f @java_object=[Ljava.lang.String;@b5950a>
Ruby String to Java Bytes and back again
bytes = 'a string'.to_java_bytes => #<#<Class:01x9fcffd>:0x40e825 @java_object=[B@3d476c>
string = String.from_java_bytes bytes => "a string"
Referencing a java.lang.Class object
If you call a Java class from JRuby and need to pass a Java class as an argument and use this form:
DoSomethingWithJavaClass(MyJavaClass.class)
You'll get this error:
TypeError: expected [java.lang.Class]; got: [org.jruby.RubyClass]; error: argument type mismatch
instead use the method: java_class
DoSomethingWithJavaClass(MyJavaClass.java_class)
Integrating JRuby and Java Classes and Interfaces
Implementing Java Interfaces in JRuby
JRuby classes can now implement more than one Java interface.
class SomeJRubyObject include java.lang.Runnable include java.lang.Comparable end
Java classes can't inherit from a JRuby class
Hopefully this feature will be added in the planned re-write of the Java integration layer for JRuby 1.2.
Material from Before JRuby 1.0
One powerful feature of JRuby is its ability to invoke the classes of the Java Platform. The following example uses JRuby 0.9.2 to create a Java JFrame with a JLabel.
Note that several package paths are magic: java, javax, org, com. Many package paths will need to be prefixed with Java::
require 'java'
JFrame = javax.swing.JFrame
JLabel = javax.swing.JLabel
frame = JFrame.new()
frame.getContentPane().add(JLabel.new("This is an example."))
frame.pack()
frame.setVisible(true)
MyClass = Java::my.package.MyClass
myClass = MyClass.new()
myClass.doit()
Note: older versions of JRuby require you to use the following code as the import preamble:
require 'java' include_class "javax.swing.JFrame" include_class "javax.swing.JLabel"
JRuby also allows you to call Java code using the more Ruby-like underscore_method_naming (the translation rule is that the Java method name is splitted by capital letters, lowercased and prepended with _) and to refer to JavaBean properties as attributes:
frame.content_pane.add(label) frame.visible = true
JRuby also lets you call Java libraries *not* included in the standard set of class libraries associated with the Java Platform. By copying jars to $JRUBY_HOME/lib or by modifying the CLASSPATH environment variable, you can access third-party Java libraries from JRuby scripts or the jirb, an interactive JRuby shell. It is also possible to add a jar to the classpath by requiring the jar (if it exists in the JRuby library load path):
# TODO Make this an example of a real library. require 'whatever.jar' com.whatever.Whatever.doSomething
Gotchas
If you have a class name ambiguity between Java and Ruby, the class name will reference the Ruby construct within the Ruby code. For instance, if you import java.lang.Thread, and then write JThread < Thread, JThread will in fact inherit the Ruby Thread object, not the Java Thread! The solution is to rather use the full Java Class name such as: JThread < java.lang.Thread

