Using Proprietary C libraries from JRuby

Let’s assume you work at big enterprise company X. Your company has assigned a new project to you, and one of its only requirements is to make sure you use this library… You have no control over the source code in the library, but you know you have to use it, because it contains some very valuable data to your users. You’ve got a ton of logic wrapped up in that library, and the company you work for already has some logic built around using that library. It is key to some department’s success. This means a couple things: (1) there’s great potential for you to provide great value to your users. It also means something else: (2) it’s probably written in C, and in general, C sucks for programmer happiness. That’s why most people programmers abandoned it — but the people who wrote that library haven’t, and it isn’t being replaced by java or ruby any day soon.

Now, before you search hotjobs or the 37signals job board for a new gig, realize that you still have some level of freedom in how you choose to go about satisfying your core requirement. This freedom includes building a user interface in a more agile-friendly language or framework. Let’s, for the sake of argument, pick some of our favorite tools to use from our development warchest. Is it possible? Could we be happy performing this task that includes a cryptic old C library? I think so. Let’s pick JRuby.

So how could you go about using JRuby to hit that old C library? You could try JRuby => C through FFI, but if the structures are quite complex and nested, you might end up debugging a lot of nasty mapping issues. JRuby can communicate with Java classes quite easily, and Java already has a way of communicating with native C code that’s well established and robust. Good old JNI. So how could we build a JRuby => Java => JNI => C connection?

It’s easier than you think. For the sake of simplicity, let’s send all data from JRuby to Java in hashes or arrays, and all data coming back from the JNI layer should be hash maps.

Wouldn’t it be nice to have a JRuby process that is able to do the following?

require 'proprietary_api'
Proprietary::API.some_calculation( some, arguments, for, calculation )

It’s quite possible, and it’s not too difficult to achieve, if you know the proper steps involved.
You’ll need:
a jar (for convenience), which contains…
a java class to declare some native methods, which will be implemented in C…
and those C methods will call the library functions you want to use.

The Java Class

import java.util.*;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.RubyString;

public class ProprietaryJavaAPI
{
  private IRubyObject rubyAPI;

  public static void init( String library ) {
    try {
      System.load( library );
    } catch ( Exception e ) {
      System.out.println( "Attempted to load " + library + ", but failed" );
    }
  }

  public ProprietaryJavaAPI(IRubyObject rubyAPI) {
    this.rubyAPI = rubyAPI;
  }

  public native Collection some_calculation( String some, String arguments, String to_the, String calculation );
}

The C code in some_calculation.c

#include 
#include 
#include "string.h"
#include "ProprietaryJavaAPI.h"
#include "proprietary_c_lib.h"

JNIEXPORT jobject JNICALL Java_ProprietaryJavaAPI_some_calculation(JNIEnv * env, jobject this, jstring some, jstring arguments, jstring to_the, jstring calculation )
{
    // here's where you call the c library and return the results... 
}

The JRuby Code that uses the Java => C layer

require 'java'

 # The jar file containing java class ProprietaryJavaAPI
require File.join( File.expand_path( File.dirname(__FILE__) ), 'proprietary_c_library_java_support' ) 

# The Java class containing definitions for native methods, like proprietary_query
include_class 'ProprietaryJavaAPI'  

 # Calling the static init method on ProprietaryJavaAPI to load the native shared object library, containing C code.
ProprietaryJavaAPI.init( File.join( File.expand_path( File.dirname(__FILE__) ), "libProprietaryAPI.so" ) ) 

module Proprietary
  class API
    class << self
      JAVA_API = ProprietaryJavaAPI.new( Proprietary::API )

      def some_calculation( *args )
         JAVA_API.some_calculation( *args )
      end
   end
end

Putting it all together
Compiling Java

    javac -target 1.6 -cp #{ ENV['JRUBY_HOME'] }/lib/jruby.jar #{Dir.glob("*.java").join(" ")}
    javah -jni ProprietaryJavaAPI
    jar cvf proprietary_c_library_java_support.jar #{Dir.glob("*.class").join(" ")}

Compiling C (Makefile)

libProprietaryJavaAPI : some_calculation.c
        gcc -m32 -fPIC -I${JAVA_HOME}/include/ -I${JAVA_HOME}/include/linux -I.. --shared -o libProprietaryJavaAPI.so -L. -lproprietary_c ProprietaryJavaAPI.c some_calculation.c 
clean :
        rm libProprietaryJavaAPI.so
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: