The Luban Java Bridge: LJB


There is one utility API need to be specifically illustrated in this chapter, which is the LJB Luban Java Bridge. LJB is the interface through which Luban can construct any type of Java object, call any Java instance and static member functions, read and write Java instance and static fields. In short speaking, you can utilize everything available in Java through LJB. The significance of Java language and the richness of its packages are the reasons that I use this whole chapter to show how LJB works.


1.1         Constructing Java Object in Luban

Luban imports all Java object as an external type java::javaobj. The construction of Java object is the same as constructing other Luban data types. Below are examples of Java object constructions.


javapie = java::javaobj(“java.lang.Double”, 3.1416);  // build a java double number

javahello = java::javaobj(“java.lang.String”, “Hello, World!”); //  java string

javanow = java::javaobj(“java.util.Date”); // obtain current date/time from java


From above you can see the way to construct Java object is actually very simple, all you need to do is to always put the Java class name as the first argument and put the arguments the class constructor needs afterwards.


1.2         Calling Java Object Member Functions

Calling Java object member function using LJB is exactly the same as calling any object member function in Luban or Java. The below are examples.


jmap = java::javaobj("java.util.HashMap");


one = jmap.get(“one”);



The above code constructs a Java hash map object and calls the “put” member function to add one key value pair into the map. Then the following code then reads out the value by looking up by the same keys with “get” member function. The last line print out the look up result to show.

You can see it is extremely simple to call Java object member function in Luban, just construct the object and call as you would do in Java itself.


1.3         Calling Static Member Function

To call a Java static member function is also simple, like below code shows.


jdumy = java::javaobj();

connection = jdumy.java_callStaticFunction( "java.sql.DriverManager","getConnection",URL, user, pswd);


The way to call Java static function is to go through a special member function of Luban java::javaobj type. The Luban java::javaobj member function name is “java_callStaticFuncion” and its arguments are Java class name, static function name followed by actual arguments for that Java static function. Because the calling Java static function does not require any specific Java object instance, we can use a Java null object as a gateway to call any Java static functions.

In the above example, we first construct a Java null object using the default constructor of java::javaobj type. The we use the “java_call StaticFunction” to call the Java static function “java.sql.DriverManager.getConnection(URL, user, pswd)”. This Java static function is to obtain a connection to a SQL server.

Below is another example of calling Java static function. This example is to split a string using Java regular expression utilities.


jnull = java::javaobj();

pattern = jnull.java_callStaticFunction("java.util.regex.Pattern", "compile", "[,\\s]+");

jresult = pattern.split("one,two, three,  four,   five"); // jresult is java String[]


The above code calls Java static function “java.util.regex.Pattern.complile()” to construct a Java regular expression pattern, then uses the pattern to split a string into an array of Java strings.


1.4         Access Java Static Fields

The way to access Java static data fields is similar to call its static member function, you need to use two special member function on the java::javaobj object, which can be Java null since the field is static. The two functions are “java_getStaticField” for reading and “java_setStaticField” for writing. Take a look at below code example.


dumy = java::javaobj();

hourfield = dumy.java_getStaticField("java.util.Calendar", "SUNDAY");



status = dumy.java_setStaticField("java.util.Calendar", "SUNDAY", 2); // try to set

std::println(obj=status);  // should print error message can’t set final field


Above code first gets the value of a Java static field “java.util.Calendar.SUNDAY” that is of value integer 1. Then it tries to set the same field value to integer 2, which is going to fail because the field is final. The code will print out the value of the field and the error status message for the field value set operation.


1.5         Access Java Instance Fields

To access Java instance data field, you need to use a valid java::javaobj instance that has that data field accessible. It can not be a Java null object. You still use special member functions to access, and the member function names are “java_getField” for reading and “java_setField” for writing. Take a look at below example code.


jdim = java::javaobj("java.awt.Dimension", 200,200);

height = jdim.java_getField("height"); // read height field



jdim.java_setField("width",100); // set width to 100



The above code constructs a Java object of Java type “java.awt.Dimension” that has two accessible fields “height” and “width” that are initialized to be both of value 200. Then the code reads the value of “height” field and prints it out. The following code then sets the “width” field to value 100 and print out the whole object to show the effect.


1.6         Checking Java Object Type

You can easily do “instanceof” checking on a Java object in Java. The similar thing you can do in Luban is “isa” operation. Though for Luban all Java objects are of type “java::javaobj” and you can not check the object’s Java type using Luban
isa” operator. The way to check the actual type of a Java object in Luban is to call another special member function “java_instanceof”. Below is one code example.



xisajavadouble = x.java_instanceof("java.lang.Double");



numberclass = x.java_getClassForName("java.lang.Number");

xisajavanumber = x.java_instanceof(numberclass);



In the above example, we first construct a Java object that is of Java type “java.lang.Double”. Then we call the “java_instanceof” function to check if the object is of type “java.lang.Double”. The function call should return a Java Boolean value true and we print it out. In the following code we do the similar type checking, yet using a different argument to call the “java_instanceof” function. This time we use a Java class object as the argument for checking. And the Java class object for Java class “java.lang.Number” is obtained from another Luban java::javaobj member function “java_getClassForName”.

java_getClassForName” is a function to load and return any Java class object using its class name. When class object loading is a frequent operation, this function becomes handly.


1.7         Java Object Is, Actually, the Java Object Reference

I have explained how to construct Java object, call its static or instance member function, access its static or instance data field plus how to do Java’s “instanceof” type checking in Luban. Everything seems smooth so far.

Though there is one important thing I haven’t explained. In Java all objects are object references, while in Luban all objects are values. How do we bridge the difference?

The solution is technical. In Luban Java Bridge, the java::javaobj type holds the real Java object reference, while from Luban side, the VALUE of the object of java::javaobj is actually the REFERENCE of that real Java object that it is holding. Luban treats java::javaobj like C treats its pointer type.

So in another word, Luban has no choice but to honor Java’s reference semantics because that’s the only thing we can get from Java. The below code shows the impact of Java object reference in Luban code.


jvec1 = java::javaobj(java.util.Vector”);

jvec2 = jvec1;




std::println(obj= jvec2==jvec2 );


Run above code will print out two identical Java vector both contain element 1, and print out the result of testing “jvec1==jvec2” as Boolean value true. Since java::javaobj type contains only Java object reference as its value, so the assignment to a new variable jvec2 does NOT actually generate a new copy of the Java Vector object. The two variables contain the reference to the same actual Java object. So the print statements print out the same vector and the equality checking ends with value true.

In order to get a new copy, you need to call the clone() member function (if available), like what you would do in Java itself. Below modified code operates on two different instances after calling the clone() function.


jvec1 = java::javaobj(java.util.Vector”);

jvec2 = jvec1.clone();




std::println(obj= jvec2==jvec2 );


Actually the object reference kinds of data types already exist in Luban before Java object. The Luban types like std::console, std::file and net::socket are all using reference in their implementations. So the assignment of file, console and/or socket to a new variable does not end up with new copy, the reference gets copied instead of the real object.


1.8         Using Java GUI Widgets and Luban InvocationQueue Class

One of the most interesting uses of Luban Java Bridge is to use Java Swing widgets to do GUI programming. With all the facilities offered by Luban Java Bridge, it is easy to construct Java GUI widget. With a little extra help from a special Java class called Luban.InvocationQueue, we’ll be able to propagate the GUI event back into Luban’s domain, and the whole thing will then just work. Below is a Luban GUI code example:


frm = java::javaobj("javax.swing.JFrame", "Hello Swinger");

jpnl = java::javaobj("javax.swing.JPanel");

jbut = java::javaobj("javax.swing.JButton", "Push me");



frm.setSize(java::javaobj("java.awt.Dimension", 300,100));


queue = java::javaobj("luban.InvocationQueue"); // new  invocation queue, line 7


listener1 = queue.newProxy("java.awt.event.ActionListener"); // line 8   

jbut.addActionListener(listener1); // line 9


listener2 = queue.newProxy("java.awt.event.WindowListener"); //line 10

frm.addWindowListener(listener2); // line 11


// Run below code between brackets in a different thread


  for(i=0;  ; ++i)


//below gets the next event in the queue, block if queue empty

            event = queue.getInvocation();



            if ( string(event.getMethod().getName()) == "windowClosing"  ) break;

            jbut.setText("Push Me "+string(i));






Run the above code and you will see a Java Swing window with a button inside labeled “Push Me”. When you click the button the label of the button will change to “Push Me X” with X to be the number of times the button has been pushed. And in the console, when button is pushed the event message will be printed.

Now let’s go through the details of this program. The first five lines of the program do the simple widget construction. It constructs a JFrame, a JPanel and a JButton. It then put the JButton into the JPanel, JPanel into the JFrame and set the size of the JFrame to 300 by 100. All these are very much like what you would do in Java, just in a slightly different syntax.

The code afterward takes care of the GUI event call back. Here we need to introduce the Java class Luban.InvocationQueue. This class is used here as the gateway to pass Java GUI event into Luban. The two most important member functions of this class are “newProxy” and “getInvocation”. Luban.InvocationQueue.newProxy() member function takes the name of a Java Interface and generates a Java proxy project to mimic the Java Interface. The generated Java proxy object can be used any Java function that takes that Java Interface. When the member function of that Java Interface gets invoked, the proxy object handles it in a very simple manner, it puts the member function method object and its arguments into a invocation queue that resides inside the original Luban.InvocationQueue.

The luban.InvocationQueue.getInvocation() function simply fetch one invocation from the invocation queue. The function blocks and waits when the queue is empty. The next new invocation will then wake up the function and it will get the new invocation and continue.

Now we can get back to how Luban code uses the Luban.InvocationQueue class to handle the GUI events. In our example, line 7 creates the InvocationQueue object. Then line 8 calls the “newProxy” member function to create a Java proxy object with the Java interface “java.awt.event.ActionListener”. Line 9 uses that Java proxy object as the call back listener for the JButton object we created before. According to my explanation above, every time the button get pressed, the member function “actionPerformed” of the Java Interface “java.awt.event.ActionListener” will be called and the Java proxy object will put the “actionPerformed” method object and the argument into the invocation queue. Line 10 creates another proxy object with Java interface “java.awt.event.WindowListener”. Line 11 uses that proxy object as the listener of the JFrame object to listen to window events like window closing. Please note you can create multiple proxy objects on the same queue. That means all the invocation upon all the different proxy objects will end up in the same queue.

 The following Luban code handling the GUI event is dispatched into a separate thread. Within the thread, it is an infinite loop. In the loop, the code first takes one event out of the invocation queue. If the queue is empty it will block and wait until next invocation comes. Then the code prints out the invocation event details. Afterwards the code check if the event is a window closing event, and break out of the loop and finish the thread if it is. Remember we have the windows event and JButton event in the same queue. Otherwise it changes the label of the JButton to include the number of loops and continue.

It is a good practice to put the event handling of one invocation queue into a separated thread so the main thread can continue when it is waiting for the next invocation event. The next example uses two queues and two threads to do pretty much the same thing as above example. Below is the code.


frm = java::javaobj("javax.swing.JFrame", "Hello Swinger");

jpnl = java::javaobj("javax.swing.JPanel");

jbut = java::javaobj("javax.swing.JButton", "Push me");



frm.setSize(java::javaobj("java.awt.Dimension", 300,100));


queue = java::javaobj("luban.InvocationQueue");


listener = queue.newProxy("java.awt.event.ActionListener");



queue2 = java::javaobj("luban.InvocationQueue");

listener2 = queue2.newProxy("java.awt.event.WindowListener");




  for(i=1; ; ++i)

// thread to handle the button click event


            event = queue.getInvocation();

            if ( event == null ) break;


            if ( string(event.getMethod().getName()) == "windowClosing"  ) break;

            jbut.setText("Push Me "+string(i));




// a different thread to handle window events




            event2 = queue2.getInvocation();


            if ( string(event2.getMethod().getName()) == "windowClosing"  )


                        q1.close();  // close another queue








When the above example is run, it does the same thing as previous example does. The difference between this example and previous example is that previous example use one invocation queue to handle the events from both the JButton and the JFrame widgets, while this example use two different queues for JButton and JFrame. And this example uses two different threads to obtain and handle the events from that two different event queues. In this example, when one thread detects that window is closing, it closes the other queue that handles the button events, so the other thread can return from “getInvocation” call with null and finish.

One trick, when you call the InvocationQueue.close() function of another queue, you need to use different variable that contains the reference to the queue used in another thread, like in above example we use variable “q1” instead of the “queue1” variable. The reason is that Luban has a thread safe feature that only allows one function call to one variable at any time. So while the “queue1” variable blocks at “getInvocation” function call, the “queue1.close()” can not get through and that will prevent both thread from finishing. So we assign “q1=queue1” first, and later we use q1 to close the queue. Because all Java objects are actually object references, this trick works.

The luban.InvocationQueue and associated luban.InvocationEvent are included in luban.jar file that is part of the Luban source and binary package.


1.9         JDBC and Regular Expression Examples

Talking to a database is commonly required for applications. Here I give one example for using JDBC interface to talk to a MySQL database server.

javadumy = java::javaobj();

drvclass = javadumy.java_getClassForName("com.mysql.jdbc.Driver");

std::println(obj="load jdbc driver:\t"+string(drvclass));


URL = "jdbc:mysql://mypc:3306/mydb";

user = "root";

pswd = "rootpassword";


conn = javadumy.java_callStaticFunction( "java.sql.DriverManager", "getConnection",URL, user, pswd);

std::println(obj="get connection:\t"+string(conn));

st = conn.createStatement();

rs = st.executeQuery ("select * from stocks");


std::println(obj="Content of stocks table:");

while ( bool( )


            stockname = rs.getString(1);


           std::println ( obj=string(stockname)+"\t"+string(stockprice));






The above example is actually very similar to any Java JDBC example code. They go through the same routine. First it loads the MySQL jdbc driver class by calling the Luban java::javaobj utility member function “getClassForName()” on a dummy java::javaobj instance. Then it calls the Java static function java.sql.DriverManager.getConnection to obtain a connection to the MySQL server with specified URL, user name and password. Then it calls the “createStatement()” member function on the connection object, then execute a query. The query is to get the content of a table named “stocks”. After it gets the results for the query, it then iterates through the results and print out contents. The program nicely closes everything at the end.


Regular expression is another commonly used tool for text processing. Below example shows how to use Java regular expression tools in Luban through LJB.


// EXAMPLE 1, string split

jnull = java::javaobj();

pattern = jnull.java_callStaticFunction("java.util.regex.Pattern", "compile", "[,\\s]+");

strtosplit = "one,two, three,,  four,   five";

jresult = pattern.split(strtosplit); // jresult is java String[]

result=vector(jresult); // convert String[] to luban vector




// EXAMPLE 2, find and replace

pattern2 = jnull.java_callStaticFunction("java.util.regex.Pattern", "compile", "cat");

original = "one cat, two cats in the yard";

matcher = pattern2.matcher(original);

match = bool(matcher.find());

jbuffer = java::javaobj("java.lang.StringBuffer");



            matcher.appendReplacement(jbuffer, "dog");

            match = bool(matcher.find());



result2 = string(jbuffer);




The above Luban code example uses Java regular expression classes to do two things, one is to split a string another is to find all “cat” words in a string and replace them with word “dog”. In the first example, it constructs a Java regular expression pattern by calling a Java static function. Then it calls the “split” member function of the pattern to split a string. Finally it converts the Java string array object to a Luban vector and prints the vector out. In the second example, it also constructs a Java regular expression pattern. Then it builds a matcher for the string to be processed with the pattern. It then goes looping to find all the occurrences of the pattern “cat” and replace it with “dog” and put the result into a Java string buffer. After the loop is done, it converts the Java string buffer to a Luban string and prints it out.


1.10     Luban and Java Data Type Conversion

There are two kinds of conversions we will discuss below, Luban to Java and vice versa.


1.10.1    Luban to Java Conversion

The Luban to Java conversion is always implicit. It happens when Luban data type is used to construct Java object and call Java function. For example, when below Luban code runs:

            Jdouble = java::javaobj(java.lang.Double”, 3.1416);

The constant 3.1416 is a Luban data type, it will be converted to a Java Double type and used as one argument to construct a Java object that happens to be also a Java Double type. The conversion is implicit because the code does not state to do that conversion explicitly.

The rules for Luban to Java type conversion is listed below.


            Luban Type                              Java Type

            double                                      java.lang.Double

            int                                             java.lang.Integer

            bool                                         java.lang.Boolean

            char                                          java.lang.Character

            string                                        java.lang.String

            map                                          java.util.HashMap

            vector                                       java.util.Vector

            set                                            java.util.HashSet

            java::javaobj                             NO CONVERSION NEEDED


One more note, the Luban to Java conversion is always a “deep” conversion. It means that if a Luban container type is converted, not only the container will be converted, every element inside the container will be converted recursively.


1.10.2     Java to Luban Conversion

Differing from Luban to Java conversion, Java to Luban conversion has to be explicit, or it will not happen. What I mean here is that any the Java object that Luban obtained from JVM with Java constructor or function calls, it will be always wrapped as a special Luban data type “java::javaobj” you can use this object just like any other Luban object without conversion.

There are situations you want to cast Luban Java object into some other Luban type. Then you need to use Luban cast expression like below:


            jvec = java::javaobj(“java.util.Vector”);

            jvec.add(1.0); // use it directly

            lubanvec = vector(jvec) // cast to Luban vector;

            foreach( x in lubanvec )



The above code converts a Java vector object into a Luban vector using a cast expression, then use foreach statement on the converted Luban vector. If no conversion happens you can still use the Java object inside variable “jvec” as it is. The above code casts the Java vector into a Luban vector only to iterate through it.

All Java to Luban conversions have to be explicit with a cast expression. Below list all Luban types that be used as cast operators and the Java types applicable to them.


            Java Types                               Luban Type


            All array types 

            + java.util.List                           vector


            java.util.Map                            map


            java.util.Set                               set


            java.lang.Boolean                     bool



            + java.lang.Character                char


            java.lang.Number                     double


            java.lang.Number                     int


Also different from Luban to Java conversion, Java to Luban conversion is always shallow, meaning when converting a Java container type to another Luban container type, only the container itself get converted, while all the elements inside container kept untouched.


1.11     Build, Run and JVM Parameters

To build and run LJB, there are some configurations need to be done. The below are the details.


·        set up environment variable JAVA_HOME before build and install

For example, you have your JDK at C:\jdk1.5.0_02 you need to specify this in POSIX format on cygwin:




On linux, you need to do the same thing.

If you don’t have JAVA_HOME env variable setup, the build script will ask you for the value of JAVA_HOME at the beginning.

On Windows with Cygwin, you also need to make sure you have the write permission to JAVA_HOME/lib directory. Luban build script will copy one file jvm.dll.a file into that directory to enable gcc to link against jvm.dll


·        Make sure jvm.dll accessible at run time

On Linux, for example you have Java installed on /usr/lib/java, you need to have the environment variable LD_LIBRARY_PATH setup like below:





On Windows, for example your Java installed at C:\jdk1.5.0, you need to put this into PATH environment variable:





Luban uses two environment variables to pass parameters it needs to start the JVM, including the place to find the jar files.

The first environment variable is “CLASSPATH”. This is the variable to contain the path to all the jar files that contains the classes that’s not a part of the JVM standard distribution. For example if you have a MySQL JDBC driver package plus the Luban InvocationQueue class to load, you can put the jar file location into CLASSPATH like below:



D:\mysqljdbc\mysql-connector-java-3.1.10\mysql-connector-java-3.1.10-bin.jar; D:\luban\javacode\luban.jar


If you have multiple jar files and/or multiple class directories, you can list them all in CLASSPATH, separate them with semicolon.

The other environment variable is “LUBAN_JVMOPTIONS”, you can put arbitrary JVM options there as far as you separate them with space. For example, you can specify JVM maximum and minimum heap size like below:


LUBAN_JVMOPTIONS=”-Xms64m –Xmx256m”