Tuesday, September 11, 2007

Friday, September 7, 2007

Calling Java from JRuby

Current JRuby is lack of detail documentation with regard to Java integration, so in certain cases it take me and my colleague a while to figure out how to do.
String to/from Java byte's array
In our recent application, we need to pass image from Ruby to image processing method written in Java. The Java method accept byte array and has result as byte array also. So we need convert Ruby String to Java byte[] and back.
The require 'java' add two methods to Ruby String class
String#to_java_bytes # instance method return Java byte[] 
String::from_java_bytes # class method return Ruby string from Java byte[]
The below code snippet demonstrate it.
require 'java'
include_class 'net.csetech.sq.image.ImageHelper'

File.open('duongpl.jpg') do |f|
  f.binmode
  @original = f.read
end

@reduced_j = ImageHelper.reduce_to_fix_size(@original.to_java_bytes,15000)
@reduced_r = String.from_java_bytes(@reduced_j)

File.open('duongpl-15k.jpg','w') do |f|
  f.binmode
  f.write(@reduced_r)
end
Ruby Array to Java Array
Other example, that we may need is to convert Ruby Array to Java Array. The require 'java' add method Array::to_java(Symbol), that convert Ruby Array to Java Array, where Symbol represent Java Class of the Array's elements. If it is omitted, the result is Java Object[].
['a','b'].to_array(:String) # return Java String[]
['a','b'].to_array # return java Object[]
More about method to_array is on JRuby Cookbook

Saturday, September 1, 2007

API design

Link to API: Design Matters
Link to How to Design a Good API & Why it Matters

ActiveRecord-JDBC 0.5 can not insert object with pre-assigned Id into Oracle Database

If you encountered problem when try to insert object with pre-assigned Id into Oracle Database in JRuby 1.0.1 with ActiveRecord-JDBC 0.5, here is quick fix
module ::JdbcSpec
  module Oracle
     def insert(sql, name = nil, pk = nil, id_value =nil, sequence_name = nil) #:nodoc:
      if pk.nil? || id_value
        execute sql, name
      else # Assume the sql contains a bind-variable for the id
        id_value = select_one(
        "select #{sequence_name}.nextval id from dual")['id'].to_i 
        log(sql, name) {
          @connection.execute_id_insert(sql,id_value)
        }
      end
      id_value
    end
  end
end
The problem is on compatibility of Oracle JDBC driver and execute_insert method of java class JdbcAdapterInternalService. The fix simply remove the usage of execute_insert method. Verification code is below
require 'rubygems'
gem 'ActiveRecord-JDBC','0.5'
require 'jdbc_adapter'
require 'active_record'

ActiveRecord::Base.establish_connection(
 :adapter  => 'jdbc',
 :driver   => 'oracle.jdbc.driver.OracleDriver', 
 :url      => 'jdbc:oracle:thin:@localhost:1521:DEV',
 :username => "scott",
 :password => "tiger",
)

class Emp < ActiveRecord::Base
  set_table_name 'emp'
  set_primary_key 'empno'
end

emp = Emp.new
emp.id = 9999
emp.save