Can Ruby Talk to Java?

Using SWIG and GCJ for a world without language boundaries

Avik Sengupta

The Tower of Babel, circa 600 BC

Lisp · Java · Python · Scheme · Perl · MFC · Pascal · Unix · C++ · Visual Basic · Ocaml · COBOL · Unix · C# · GTK+· Win32 · Basic · Ada · .NET · KDE · Windows · PHP · Swing · Service Oriented Architectures · Parrot · Smalltalk · Linux · GTK# · Ruby · Fortran · POSIX · XML · Delphi · JavaScript · ECMA Script· VB# · Eiffel · C ·

Frameworks and Languages, circa 2004 AD

Today, we’ll talk about bridging these two

Lisp · Java · Python · Scheme · Perl · MFC · Pascal · Unix · C++ · Visual Basic · Ocaml · COBOL · Unix · C# · GTK+· Win32 · Basic · Ada · .NET · KDE · Windows · PHP · Swing · Service Oriented Architectures · Parrot · Smalltalk · Linux · GTK# · Ruby · Fortran · POSIX · XML · Delphi · JavaScript · ECMA Script· VB# · Eiffel · C ·

Why?

Heterogeneous platforms are a fact of life, can’t wish it away!

Leverage existing code base

Use libraries from the other language

Migrate in steps

Program in your favorite language

FOSS

Many complex interoperablity solution are free / open source

Major source of innovation

Business model encourages interoperability!

Have the source!

Ruby-Java Interoperability Options

Remote invocation

via Web Services

via custom marshalling

J-Ruby (Ruby interpreter in Java)

JNI

GCJ Basics

Java front end to GCC

Uses common GCC backend – ie, compile to native object code

SWIG Basics

Simplified Wrapper and Interface Generator

Connects C/C++ programs with many high level languages

Perl, PHP, Python, Tcl, Ruby, PHP,C#, Common Lisp, Java, Modula-3, OCAML, Scheme, Java

Easiest way to create language bindings

DEMO

Use POI (a java library to create Excel files) from Ruby

Compile

Compile Java code to .o with gcj

Generate .h interface files with gcjh

SWIG Typemaps

Define the interface between C and Ruby

Similar syntax to a .h file

%module poi4r

%{

#include <gcj/cni.h>

#include <java/lang/Object.h>

.....

#include "org/apache/poi/hssf/usermodel/HSSFWorkbook.h”

......

#include "org/apache/poi/hssf/usermodel/HSSFWorkbook.h

.....

Convert types

Convert java string to ruby

VALUE j2r(jstring js){

if (!js) {

return Qnil;

}

else {

jint len = JvGetStringUTFLength(js);

char buf[len + 1];

JvGetStringUTFRegion(js, 0, len, buf);

buf[len] = '\0';

return rb_str_new2(buf);

}

}

Similar for other types

Define wrapped interfaces

namespace org {

namespace apache {

namespace poi {

namespace hssf {

namespace usermodel {

%nodefault;

class HSSFWorkbook : public ::java::lang::Object {

public:

HSSFWorkbook();

jstring getSheetName(jint);

......

Memory Management

Both Java and Ruby use Garbage Collectors, memory leaks easy to prevent -- ensure no dangling references

Premature garbage collection equally erroneous -- Sometimes, you want to see “OutOfMemory” errors!

Understand semantics of the API you are wrapping

API's with single entry points easier to wrap

Memory Management: Object creation

%exception org::apache::poi::hssf::usermodel::HSSFWorkbook::HSSFWorkbook{

try {

$action

store_reference(result);

} catch (java::lang::Throwable *e) {raise_ruby_error(e);}

}

.....

static void store_reference(jobject object) {

java::lang::Integer *ji =new java::lang::Integer(java::lang::System::identityHashCode(object));

jobject jo = objects->get(ji);

if (!jo) { objects->put(ji,object); }

}

Memory Management – Object freeing

%freefunc org::apache::poi::hssf::usermodel::HSSFWorkbook "free_java_obj";

.....

static void free_java_obj(void* arg1) {

jobject object =(jobject) arg1;

java::lang::Integer *ji =new java::lang::Integer(java::lang::System::identityHashCode(object));

jobject jo = objects->get(ji);

if (jo) {objects->remove(ji);}

}

Map errors from Ruby to Java

%exception {

try { $action } catch (java::lang::Throwable *e) {

raise_ruby_error(e); } }

.....

static void raise_ruby_error(java::lang::Throwable *e) {

java::io::StringWriter *buffer = new java::io::StringWriter();

java::io::PrintWriter *writer = new java::io::PrintWriter(buffer);

e->printStackTrace(writer); writer->close();

jstring message = buffer->toString();

jint len = JvGetStringUTFLength(message);

char buf[len + 1];

JvGetStringUTFRegion(message, 0, len, buf);

buf[len] = '\0';

rb_raise(rb_eRuntimeError, "error calling poi \n %s", buf);

}

Initialisation

Initialise the JVM, and load classes at startup

%init %{

JvCreateJavaVM(NULL);

JvAttachCurrentThread(NULL, NULL);

objects = new java::util::Hashtable();

java::util::Hashtable *props = (java::util::Hashtable *)

java::lang::System::getProperties();

props->put(JvNewStringUTF("inRuby"), objects);

JvInitClass(&org::apache::poi::hssf::usermodel::HSSFFont::class$);

.....

%}

Status

Works well for most projects

GUI applications not so well supported

Limited by GNU Classpath, immature Swing implementation

If you are interested in free JVM’s, this is where you contribute!

If you test, you will be safe!

Questions?

Curiosity killed the cat... or did it?

More Info

GCJ Website

http://gcc.gnu.org/java/

SWIG Website

http://www.swig.org/

Ruby Wrapper on Jakarta-POI

http://jakarta.apache.org/poi/poi-ruby.html

This Presentation

http://www.sengupta.net/talks/

Ruby - Java

Avik Sengupta

2004-11-15T17:06:34