In: |
ruby/comm_abstract.rb
ruby/comm_xmlrpc.rb ruby/jbridge.rb ruby/comm_bstream.rb ruby/jlambda.rb |
This module provides a communication facility between Ruby and Java. The current implementation is written by pure Java and pure ruby. You can use ruby language and gain Java power easily.
require 'yajb/jbridge' include JavaBridge jimport "javax.swing.*" :JOptionPane.jclass.showMessageDialog( nil, "Hello World!") # this Java window sometimes shows behind the other window.
You can use Java GUI components as you use on Java. See sample scripts:
Writing scripts in UTF-8, you can use multibyte code correctly.
POI sample demonstrates multibyte handling.
This package contains the javassist, the byte engineering tool library. You can make the POJO implementation from scratch.
See javassist website, www.csg.is.titech.ac.jp/~chiba/javassist/index.html
When you call a communication method for the first time, this module starts up JVM and constructs the bridge between Ruby and Java. You can configure JVM and Bridge parameters via hash object, as follows:
JBRIDGE_OPTIONS = { :classpath => "$CLASSPATH", # this record will be evaluated as `echo #{classpath}`. :jvm_path => "java", # if you need, set fullpath to the JVM. # if nil, the jbridge will not start the JVM by itself. :jvm_vm_args => "", # VM argument. ex: "-Xmx128m" :jvm_log_file => nil, # output from JVM. ex: "log.txt" :jvm_log_level => "normal", # JVM debug level. [debug,verbose,normal,warning,error] :bridge_log => false, # Ruby debug output. true or false :bridge_driver => :bstream, # communication driver (:xmlrpc or :bstream) :xmlrpc_bridge_port_r2j => 9010, # communication port: ruby -> java :xmlrpc_bridge_port_j2r => 9009, # communication port: java -> ruby :xmlrpc_bridge_opened => :kill, # If the port is using, how the program deals the port: # :kill : try to kill the previous port and open new port # :abort : abort program # :reuse : use the previous port :bstream_bridge_port => nil, # communication port for bstream driver. # if nil, the available port will be searched automatically. }
:xmlrpc driver is simple implementation to check the communication protocol. Although the XMLRPC communication library is easy to get and use and the implementation, the speed is slow. The slowness is due to the XML parsing and writing.
:bstream driver is second implementation improved the communication speed. This driver tranports binary data form on the TCP socket. So the speed of the :bstream communication is two times faster than that of :xmlrpc driver.
The numerical values, String and Array objects are transfered to Java side so as to maintain their precision. (the xmlrpc driver loses the precision of the floating point values because of transfoming text encoding.) The transformation between the Ruby and Java is done as follows:
Java Ruby ---------------------------------------- byte(Byte) short(Short) int(Integer) Fixnum,Bignum,Float long(Long) (jbridge transforms dynamically) float(Float) double(Double) BigDecimal String String Object[] Array (content objects are applied transformation recursively) primitive array Array (typed array, see text)
The instances of Fixnum, Bignum and Float are encoded by driver and tranfored to Java side. Then, the variable type are detected by its own value size and arguments types of called methods dynamically.
The other objects in Ruby can not be transfer into Java. On the other hand, if the other object in Java are transfered, the proxy objects are created to manipulate the Java objects from the Ruby side. Please see the next section for more details.
The transfer mechanism of the array object is very complecated. Generally, jbridge can not exactly construct an array object in Java, because numerical values never tell its own Java type. (for example, the value of "1" can be hold byte, short, int, long and double in Java.) So, in many cases, the array objects are transformed into Object[] values. Only if an array consists of String, an object of String[] is created in Java.
If you want to transfer an array as a primitive array, you can use typed array notation. The first element in the array specifies the primitive type:
[:t_int4, 1, 2, 3, 4] (Ruby) --> new int[]{ 1, 2, 3, 4 } (Java).
You can use following type-symbols,
All created java objects, such as created by jnew, jextend, jstatic and return values, are holded by JavaBridge server. Those objects has proxy id to delegate the method calling between the Java and ruby. If a proxy object is garbage collected in the ruby side, the JavaBridge sends an unlink message to the Java side to remove the corresponding object from the repositry in Java.
The overriding ruby objects created by jextend method are never removed automatically, because the JavaBridge can not decide when the object should be removed. You can remove the objects, calling the "junlink" method manually.
The string data is transported to the Java without any modification. The Java side interprets the string data as encoded in UTF-8. So, you need to adjust your string encoding to UTF-8, before you call the Java methods.
When your Ruby program finishes (that is the termination of main thread), the communication bridge is also killed. If you want to wait for the message from the JVM, such as GUI events, you need to stop the main thread. For convenience, you can call "stop_thread" method to hold the bridge communication. Then, you can restart the main thread to call "wakeup_thread".
In the present implementation, because the arguments and return values are transported by the serial stream, the calling Java method takes longer time than the calling pure Ruby methods.
If you feel your code is too slow, you consider using jlambda or JClassBuilder. Because they are run on the Java, you can reduce the time of the trasportation of values.
In the future, replacing the communication driver by JNI may improve the speed.
The utility of jlambda and JClassBuilder works through the javassist. So, your dynamic java code is restricted by the javassist compiler. For example, you can not write any comment, inner class and labeled jump in jlambda code. Please see the javassist documents for more details.
The design of the jlambda utility is not good, because I have little idea. Please give me the advice or codes.
The YAJB communicates with the Java, using the following protocol that consists of basic remote procedure calls.
Creating an proxy instance that can override public and protected methods of the class and interfaces. Adding the singleton method to the instance, you can implement the Java interfaces or abstract class in Ruby.
The class name parameter can take zero or one class and arbitrary number of interfaces. Those class names are specified by Symbol(only one) or String(separated by ","). *Ex: "java.awt.event.ActionListener,java.awt.event.WindowAdaptor"
If Ruby class overrides the Java method that has the same, the Java method delegates the operation to the Ruby method. The abstract method delegates Ruby method to execute immediately.
Corresponding notatin: :SomeJavaClassName.jext or :some_package_ClassName.jext
jimport is almost similar to "import" statement of Java. Ex:
The later entry is given a priority.
Creating an instance of the concrete class. The class name can be specified by String and Symbol. Ex: "java.awt.Point" corresponds width :java_awt_Point.
The return value is the proxy object for the instance of the Java.
Corresponding notatin: :SomeJavaClassName.jnew or :some_package_ClassName.jnew
Creating a static reference to the Java class. Through the reference, the any static methods and fields can be called by Ruby.
Corresponding notatin: :SomeJavaClassName.jclass or :some_package_ClassName.jclass
Remove the proxy object in the repositry of Java side. After calling this method, the proxy object can not call any method of the corresponding java object.
This method remove the link between the proxy object and jbridge object repositry so as for the proxy object to be garbage collected. This method never remove the proxy object.
Stopping the main thread so as not to finish the Ruby program. (The GUI programs can wait for messages from the JVM.)
Calling "wakeup_thread", the Ruby program resumes the main thread. (Before calling "exit" to terminate the Ruby program, the main thread should be on the running state.)