Gem #55: Introduction to Ada / Java Interfacing
by Quentin Ochem —AdaCore
Let's get started…
Suppose we have an API (Application Program Interface) on the Ada side that we would like to call from Java. This API may be based on a set of types and functions, for example:
package API is type R is record F1, F2 : Integer; end record; procedure Print (Str : String; V : R); end API;
Our purpose is to create an object of type R from Java, and then pass that object to the Ada Print procedure, along with a string parameter.
Generating the binding code
Now we need to generate the Java code as well as the extra JNI layer that will connect to the Ada code. This can be done using the ada2java tool, called as follows:
ada2java api.ads –b test –o ada –c java –L my_lib
Since the tool only generates the binding to the package, it only needs access to the specification (the api.ads file).
Then, for organizational purposes, we provide the name of the base package for all the generated Java classes, using the –b flag. Here, all the generated classes and packages will be children of the Java package “test”.
The –o and –c flags define the directory where, respectively, the Ada and the Java generated code will be stored. Here, we put the Ada code in the directory ada/ and the java code in the directory java/.
Finally, the main Ada code (here, the package My_Package) and the generated glue code needs to be compiled in a shared library that will be loaded by Java. In order to simplify the process, it is possible to generate a GNAT project file for input to gprbuild that will handle compilation of this library, by using the –L switch.
Writing the Java code
If you take a look at the content of the java/ directory, you willl find a bunch of classes, in particular:
test.API.R test.API.API_Package test.Standard.AdaString
These are all the classes needed by our application. test.API.R maps to the Ada R object, test.API.API_Package contains all the subprogram declarations that come from the API package and cannot be defined as members of other types, and test.Standard.AdaString is an automatically generated class that maps the standard String type.
Observe that the test.API.R class comes with accessors and modifiers for the F1 and F2 fields, and includes a default constructor. Note also that test.Standard.AdaString comes with a constructor based on java.lang.String.
It is now time to write the main Java class:
import test.API.R; import test.API.API_Package; import test.Standard.AdaString; public class My_Main { public static void main (String [] argv) { R v = new R (); v.F1 (10); v.F2 (15); API_Package.Print (new AdaString (“Hello”), v); } }
That’s it – this works just as if you had written a call directly from Java to Ada!
Compiling and running the code
The last step is to compile and run the code. Since we’ve already created the GNAT project file for the Ada code, compiling it only requires running gprbuild on it:
gprbuild –p –P ada/my_lib.gpr
Now you need to adapt either the PATH (on Windows) or LD_LIBRARY_PATH (on Linux/Solaris) in order to list the ada/lib directory, for example:
LD_LIBRARY_PATH=`pwd`/ada/lib/:$LD_LIBRARY_PATH export LD_LIBRARY_PATH
The generated java code must be in your CLASSPATH, as well as the main subprogram that you’ve written, and the lib/ajis.jar file of your AJIS installation. Let’s assume that you’re running on Linux, and your Java main is at the same location as the api.ads file. You’ll need:
CLASSPATH=`pwd`:`pwd`/java:<your AJIS installation>/lib/ajis.jar:$CLASSPATH
Compiling and running the Java code is now straightforward:
javac My_Main.java java My_Main
Note that the library that we’ve compiled will be automatically loaded by the generated bound code.
Upcoming Gems will show more advanced usage of GNAT-AJIS, including using callbacks, OOP, exceptions, and memory management.