API for Ruby/Java integration through JNI (feedback needed!)

OK, I’m going quite fast so I need now to decide how Java classes and
objects are gonna work in Ruby. I’d appreciate some comments on what
kind of mapping you’d like.

At the moment I have the following classes:

Low-level (won’t change much)

···

=============================
Implemented in C.

  • RJNI::Object low-level wrapper for a jobject (include jstring and
    others, including arrays). Accepts #to_s with the
    obvious semantics.
  • RJNI::Class idem for jclass. implements #to_s
  • RJNI::MethodID idem for jmethodID “”
  • RJNI::FieldID idem for jfieldID “”
  • RJNI::Primitive wrapper for native types (boolean, char, byte, short,
    int, long, float, double). Transform to Ruby w/ to.i,
    to_f and to_b.
  • RJNI::JVM object that represents the JVM and exposes the JNI
    interface

This is the low-level part, nothing more than a simple mapping from JNI
to Ruby. This allows to make use of JNI from Ruby, as in
include RJNI
vm = JVM.new “-cp .”
klass = vm.find_class(“Simple”)
constructor = vm.get_method_id(@klass, “”, “()V”)
obj = vm.new_object(klass, constructor)

methid = vm.get_method_id(klass, “doStuff”, “()Llang/java/String;”)

this gets the method id for method

String doStuff()

str = vm.call_object_method(obj, methid)
puts "Result: " + str.to_s

As you can see, this is very low-level, just as JNI.
This layer should expose all of JNI and allow you to do everything. It
should be similar in spirit to Win32API.

High-level (in a state of flux)

This one is at the moment built upon the low-level layer, in Ruby.
It could be done in C too, but I want to fix the API before.

  • Reflect::Class provides the list of instance methods
  • Reflect::Object understands and dispatches the appropriate Java methods

This layer uses the JNI services of the former and transparently
dispatches methods as needed via reflection. Right now it works
as follows:

reflector = Reflect.new vm # create the “reflector”, only need one
theklass = reflector[“Simple”]
object = theklass.new # mimic Ruby’s object creation

from now on, can call methods on object and they are automatically

transformed into the appropriate vm.call_xxx_method as required

plus associated logic, depending on arguments and return value

str = object.doStuff
puts "Result: " + str.to_s

Now, the things I need feedback on:

  • static methods: should they magically pop up in theklass, so that they
    work as in Class objects in Ruby?
  • should I provide #instance_methods and such?
    at the moment I’ve made #java_instance_methods that returns an Array
    with RJNI::Object objects that represent the methods.
  • should instance methods be directly invocable such as in the example
    above, or by doing object.send_java(“doStuff”, *args) or such?
    It is not as pretty, but OTOH this way there’s no name collisions.
  • same as the latter w/ static methods (which would look like singleton
    methods of the Class object)

Note that the method dispatching magic only works w/ Reflect::Object
objects, so that RJNI::Object are lower-level and don’t accept direct
message passing (you have to use vm.call_xxx_method w/ them).

Mapping the Reflection API

a third possible API would be just mapping all of the Reflection, ie.
creation something like RJNI::JavaReflection::{Class,Method,Field…}.
it is only a little bit more convenient than using the JNI layer
directly, but maybe somebody might appreciate it?

Comments?

If you’re confused, here’s some help :slight_smile:
Select one (mark your option w/ an X):

___        provide transparent mapping for everything, don't care
           about name clashes (or indicate solution for them)

___        do everything through send_java(method_name, *args)


___        forget about the "higher-level" thing and simply map
           the Reflection API 1:1 from Java to Ruby.


_ _

__ __ | | ___ _ __ ___ __ _ _ __
'_ \ / | __/ __| '_ _ \ / ` | ’ \
) | (| | |
__ \ | | | | | (| | | | |
.__/ _,
|_|/| || ||_,|| |_|
Running Debian GNU/Linux Sid (unstable)
batsman dot geo at yahoo dot com

Do you mean to say that I can read mail with vi too? :wink:
Didn’t you know that?
:r /var/spool/mail/jk
– debian-mentors

First of all, this is a most perfect project. To be able to “script” a
Java application or make use of Java frameworks from Ruby will go a
long way to making Ruby enterprise-friendly.

next…

···

On Wednesday, June 25, 2003, at 02:34 PM, Mauricio Fernández wrote:

___        forget about the "higher-level" thing and simply map
         the Reflection API 1:1 from Java to Ruby.

I would just do this as complete as possible, and release it. There
may be varying types of higher-level APIs that folks want to construct
(myself included), but without the base, nothing can be done.

Thanks for your (incredibly fast) efforts.

-rich

OK, I’m going quite fast so I need now to decide how Java classes and
objects are gonna work in Ruby. I’d appreciate some comments on what
kind of mapping you’d like.

here we are

reflector = Reflect.new vm # create the “reflector”, only need one
theklass = reflector[“Simple”]
object = theklass.new # mimic Ruby’s object creation

reflector sounds wierd to me… somthing better? :slight_smile:

Now, the things I need feedback on:

  • static methods: should they magically pop up in theklass, so that they
    work as in Class objects in Ruby?

it would be cool if Reflect.new worked like Class.new, so that static
java methods would automagically become class methods. anyway, a
solution that just mimic this would be good enough.

  • should I provide #instance_methods and such?
    at the moment I’ve made #java_instance_methods that returns an Array
    with RJNI::Object objects that represent the methods.
    are you talking about ruby reflection’s apis? anyway, I supose yes
  • should instance methods be directly invocable such as in the example
    above, or by doing object.send_java(“doStuff”, *args) or such?
    It is not as pretty, but OTOH this way there’s no name collisions.

what kind of name collision could happen ?

anyway I have no idea about the possibility to do this stuff, so
forget my desires if you wish :slight_smile:

···

il Thu, 26 Jun 2003 03:34:10 +0900, Mauricio Fernández batsman.geo@yahoo.com ha scritto::

reflector = Reflect.new vm # create the “reflector”, only need one
theklass = reflector[“Simple”]
object = theklass.new # mimic Ruby’s object creation

reflector sounds wierd to me… somthing better? :slight_smile:

it’s just a variable name :slight_smile: Now, if you don’t like Reflect, I can still
change it.

Now, the things I need feedback on:

  • static methods: should they magically pop up in theklass, so that they
    work as in Class objects in Ruby?

it would be cool if Reflect.new worked like Class.new, so that static
java methods would automagically become class methods. anyway, a
solution that just mimic this would be good enough.

It does now. Static methods seem to become singleton methods of the
class objects. You can do
aklass.java_instance_methods
to get an array of Method objects, and
aklass.java_methods
to get the static methods (singleton methods) of the class.

Moreover, classes are objects too. You can get the class of a class, too.
Take a look at this example:

Simple.java

···

On Fri, Jun 27, 2003 at 05:39:28AM +0900, gabriele renzi wrote:

package net.thekode.rjni;

public class Simple {
public String doStuff(String a) {
return “I was called with argument \”" + a.toString() + “\”" ;
}

public String doStuff(String a, String b) {
	return a + b;
}

public boolean compareStrings(String a, String b) {
	System.out.println("I was passed " + a + " and " + b);
	return a.equals(b);
}

public int compare2(String a, String b) {
	System.out.println("I was passed " + a + " and " + b);
	return a.compareTo(b);
}

public int sum(String a, String b) {
	System.out.println("Adding " + a + " and " + b + " (inside Java!!!)");
	int i = Integer.parseInt(a) + Integer.parseInt(b);
	System.out.println("The result will be: " + i);
	return i;
}

public static void superTest() {
	System.out.println("Super static test!!");
}

}

test_reflect.rb

require ‘reflect’
require ‘rjni’

RJNI::JVM.new #“-cp .”

reflector = RJNI::Reflect.new
sclass = reflector[“net.thekode.rjni.Simple”]
puts “Class: #{sclass}”

puts “Instance methods:”
puts “=” * 40
sclass.java_instance_methods.each { |x| p x }
puts “=” * 40

puts

puts “Class methods:”
puts “=” * 40
sclass.java_methods.each { |x| p x }
puts “=” * 40

sclass.superTest ### Ruby way!!!

puts

o = sclass.new
puts “Created object <#{o.to_s}> of class <#{sclass}>”
puts “Getting metaclass of <#{o}>”
metaclass = sclass.java_class
puts “Metaclass <#{metaclass}>”
puts “Listing methods of the class (using metaclass.getMethods):”
puts(“=” * 40)
metaclass.getMethods.to_a.each { |x| p x }
puts(“=” * 40)
puts “Now let’s execute some methods…\n”
r = o.doStuff(“foo passed from Ruby!!!”)
puts “Result: #{r}”
r = o.doStuff("hello, ", “world!”)
puts “Result: #{r}”
r = o.compareStrings(“ruby”,“matz”)
puts “Result: #{r.to_b}”
r = o.compareStrings(“matz”,“matz”)
puts “Result: #{r.to_b}”
r = o.compare2(“ruby”,“matz”)
puts “Result: #{r.to_i}”
r = o.compare2(“matz”,“matz”)
puts “Result: #{r.to_i}”
r = o.sum(“1”, “1”)
puts “Result: #{r.to_i}”

Output

Class: net.thekode.rjni.Simple
Instance methods:

public java.lang.String net.thekode.rjni.Simple.doStuff(java.lang.String)
public java.lang.String net.thekode.rjni.Simple.doStuff(java.lang.String,java.lang.String)
public boolean net.thekode.rjni.Simple.compareStrings(java.lang.String,java.lang.String)
public int net.thekode.rjni.Simple.compare2(java.lang.String,java.lang.String)
public int net.thekode.rjni.Simple.sum(java.lang.String,java.lang.String)
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()

Class methods:

public static void net.thekode.rjni.Simple.superTest()

Super static test!!

Created object net.thekode.rjni.Simple@1004901 of class <net.thekode.rjni.Simple>
Getting metaclass of net.thekode.rjni.Simple@1004901
Metaclass
Listing methods of the class (using metaclass.getMethods):

public java.lang.String net.thekode.rjni.Simple.doStuff(java.lang.String)
public java.lang.String net.thekode.rjni.Simple.doStuff(java.lang.String,java.lang.String)
public boolean net.thekode.rjni.Simple.compareStrings(java.lang.String,java.lang.String)
public int net.thekode.rjni.Simple.compare2(java.lang.String,java.lang.String)
public int net.thekode.rjni.Simple.sum(java.lang.String,java.lang.String)
public static void net.thekode.rjni.Simple.superTest() public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()

Now let’s execute some methods…
Result: I was called with argument “foo passed from Ruby!!!”
Result: hello, world!
I was passed ruby and matz
Result: false
I was passed matz and matz
Result: true
I was passed ruby and matz
Result: 5
I was passed matz and matz
Result: 0
Adding 1 and 1 (inside Java!!!)
The result will be: 2
Result: 2

  • should I provide #instance_methods and such?
    at the moment I’ve made #java_instance_methods that returns an Array
    with RJNI::Object objects that represent the methods.
    are you talking about ruby reflection’s apis? anyway, I supose yes

It’s in fact redundant as you can get most things through JNI, and you
can always use JNI to use the reflection API :slight_smile:

  • should instance methods be directly invocable such as in the example
    above, or by doing object.send_java(“doStuff”, *args) or such?
    It is not as pretty, but OTOH this way there’s no name collisions.

what kind of name collision could happen ?

classes in Java with methods like object_id, instance_methods, class,
method_missing, etc…

anyway I have no idea about the possibility to do this stuff, so
forget my desires if you wish :slight_smile:

everything doable, by experience: I have done most of it by now :slight_smile:


_ _

__ __ | | ___ _ __ ___ __ _ _ __
'_ \ / | __/ __| '_ _ \ / ` | ’ \
) | (| | |
__ \ | | | | | (| | | | |
.__/ _,
|_|/| || ||_,|| |_|
Running Debian GNU/Linux Sid (unstable)
batsman dot geo at yahoo dot com

We are MicroSoft. You will be assimilated. Resistance is futile.
– Attributed to B.G., Gill Bates

___ forget about the “higher-level” thing and simply map
the Reflection API 1:1 from Java to Ruby.

I would just do this as complete as possible, and release it. There
may be varying types of higher-level APIs that folks want to construct
(myself included), but without the base, nothing can be done.

I second that.

Thanks for your (incredibly fast) efforts.

I second that, too :).

That is awesome!

What kind of performance penalty is there accessing the Java objects
in this way?

Walt

···

Walter Szewelanczyk
IS Director
M.W. Sewall & CO. email : walter@mwsewall.com
259 Front St. Phone : (207) 442-7994 x 128
Bath, ME 04530 Fax : (207) 443-6284


It does now. Static methods seem to become singleton methods of the
class objects. You can do
aklass.java_instance_methods
to get an array of Method objects, and
aklass.java_methods
to get the static methods (singleton methods) of the class.

well, I suppose having the java_(reflecting) method separated from the
ruby one could be useful… but I wonder if someone would prefer full
consistency with “the rest of the world”.

Possibly a JRNI::FULL_MIMIC flag for Reflect.new?
:slight_smile:

what kind of name collision could happen ?

classes in Java with methods like object_id, instance_methods, class,
method_missing, etc…

I suppose that this kind of collision should be handled by the user.
I strongly prefer using
my_java_obj.method
over
my_java_obj.send( :method)

I suppose the name collision for common methods (like ‘class’) would
be few
(looking at Class or Object, for example, I can’t see a cooomn method)
Moreover, common methods such as Object.class or Array.length would
collide but would give an equal result…

BTW, if someone is taking the approach of using a remote Java object
he should be aware if some needed ruby method gets messed up from
java.

Possibly a best way could be aliasing colliding methods such that a
colliding :meth would make java_obj respond to :ruby_meth and :meth
(or :java_meth and :meth).
Ugly, but this should be really rare imVho

anyway I have no idea about the possibility to do this stuff, so
forget my desires if you wish :slight_smile:

everything doable, by experience: I have done most of it by now :slight_smile:

Yeah,
but now I’m waiting for the CORBA binding in the next few days :wink:

···

il Fri, 27 Jun 2003 06:05:28 +0900, Mauricio Fernández batsman.geo@yahoo.com ha scritto::

What kind of performance penalty is there accessing the Java objects
in this way?

Performance can become very fast completely unuseable. The latest Java
release reduced the reflexion overhead by a factor 10-20. But it
still hurts to go through all the security checks of the java platform.
This was the reason for SUN to use this anonymous inner classes in
Swing instead to go through the Reflextion API.

But if you want to interact with a high level Java API it will be
okay.