3. Using ada2java to Generate Java Classes¶
The ada2java
tool takes one or more Ada package specs
and produces as output a Java ‘binding’ to these packages, implemented
through JNI.
The binding consists of a set of Java classes, with methods that access
the Ada package’s visible entities.
More specifically, ada2java
generates two sets of source files
as output:
The Java classes that make up the binding, and
The necessary Ada ‘glue code’ that hides the details of how JNI is used for interfacing between Ada and Java.
You will need to compile the Java files to bytecodes for execution on a JVM, and you will need to compile the Ada files to native code in a dynamic library.
This chapter explains how to use the ada2java
tool and
describes the mapping from package spec contents to Java classes.
3.1. Using the Tool¶
The ada2java
tool is invoked with at least one input file,
and any number of switches, in any order:
$ ada2java {<switch> | <input-file>} <input-file> {<switch> | <input-file>}
Each <input-file>
must be the name of a source
file for an Ada package spec (including the extension).
The following <switch>
values are allowed:
--help
Display help
-c <JavaClassOutputDirectory>
The root directory used as the destination for the output classes. The directory will be created if it does not already exist. In the absence of this switch, the current directory is used. See below for the relationship with the
-b
switch.
-b <BaseJavaBindingPackage>
The base package for the generated Java classes; this will be relative to the directory specified in the
-c
switch, or relative to the current directory if no-c
switch was supplied.
-o <AdaGlueOutputDirectory>
The destination directory for the ‘glue’ packages (
ads
andadb
files) generated byada2java
. The current directory will be used if this switch is not supplied. The generated packages will need to be compiled into a dynamic library.
-P <ProjectFile>
The project file that applies to the processing of the
<input-file>
s submitted toada2java
. This can specify compiler switches, source directories, etc.<ProjectFile>
must be a ‘flat’ project (sources from ‘with’ed projects are not yet supported).
-L <LibraryName>
A mechanism for automating the loading of the native Ada dynamic library in Java. This switch causes the generation of a project file
<LibraryName>.gpr
in the directory specified by the-o
switch (or in the current directory if the-o
switch was not supplied). The resulting project file can be submitted togprbuild
to build the dynamic library:$ gprbuild -p -P LibraryName.gprwhich will generate a
lib/
subdirectory that contains the filelib<LibraryName>.so
(Solaris, Linux) or<LibraryName>.dll
(Windows). This library will be loaded automatically whenever one of the Java classes produced byada2java
is loaded; there is no need for the user to explicitly include an invocation ofSystem.loadLibrary
.
-M <MainName>
A mechanism for automating the creation of an Ada main subprogram, embedding both the native code and a JVM. See Compiling as an Ada Main Subprogram for more details. Implies
-link-method=register_natives
.
--main-class=<java main class>
Changes the name of the java main class to use, in case the
-M
switch is used. See Compiling as an Ada Main Subprogram for more details.
--link-mode=(export|register_natives)
The Java virtual machine has two ways of discovering the functions declared in the native environment. Either it checks the correspondence between the exported symbol and the Java native declaration name (
export
mode), or the JNI code registers manually the symbols using theRegister_Native
JNI function (register_natives
mode). Note that if the code is not in a shared library but compiled with a main native subprogram, then onlyregister_natives
mode will work.
--library-kind=(dynamic|encapsulated)
Set the library generation method: normal or standalone (used by -L). Ada2Java can create two different kinds of library: dynamic or encapsulated. Dynamic is the default mode and generate a dynamic library which depends on GNATJNI and AJIS libraries on one hand, and the GNAT run-time on the other hand whereas an encapsulated library is autonomous: it contains all the necessary symbols from the three dependencies to be standalone.
--bound-package-root=<root package name>
Set the name of the root glue Ada packages (default is JNI_Binding).
--bound-package-suffix=<package suffix>
Set the suffix of the glue Ada packages (default is _JNI).
--no-locking[-finalize]
--locking[-finalize]-(check|protect)
Sets the default locking for subprograms. See Thread Safety.
--[no-]attach-(parameter|access|controlling|ada2005|constructor|variables)
Sets the default attachment policy. See Managing Attachment to Java Proxies
--[no-]assume-escaped
Controls whether checks for object ownership are enabled. See Restrictions on Proxy-Owned Objects Passed to Subprograms
--[no-]java-enum
Controls whether Java enumerations should be used to bind Ada enumerations, or if static integers should be used instead (Java enumerations are the default).
--[no-]java-naming-convention
Controls whether java naming convention (Get_Member, Is_Member, Set_Member) should be used to access record’s fields or package’s variables. By default legacy (Member() Member(New_Value)) naming convention is used.
--[no-]check-native-still-allocated
Controls whether ada native object still allocated checks should be done when calling ada from java. By default checks are generated.
--[no-]array-iterator
Controls whether bound arrays are implementing Iterable interface (Generating Iterable interface is the default).
--[no-]iterable-aspect-iterator
Controls whether records having iterable aspect are implementing Iterable interface (Generating Iterable interface is the default).
--[no-]collection-iterator
Controls whether records having cursor returned by First function that are implementing Has_Element, Element and Next functions should implements java Iterable interface (Generating Iterable interface is the default).
--[no-]iterator-optimization
Controls whether java iterator for ada collections should use an optimized implementation. Such implementation requires one ada call per collection element instead of three.
(Generating optimized iterators is the default).
--[no-]function-alias
Controls whether AJIS generates function as alias of procedure having only one out mode parameter. (generation off is the default).
--[no-]access-optimization
Controls whether AJIS uses ada object address instead of java one when calling ada code from java. (optimization on is the default).
--[no-]unaliased-access
Controls whether ada2java is allowed to create proxies on unaliased data.
--no-unaliased-access
is default. See Aliasing
--gnatdoc-dir=dir
Defines the location of the gnatdoc output files used to copy ada declarations comments to generated java interface layer.
To generate these gnatdoc output files you need to:
Install a recent version of GNATdoc (shipped as part of the GPS package)
Before running
ada2java
tool
Generate GNATdoc output files using
gnatdoc
tool.$ gnatdoc --enable-build --output=cm -P project.gprgnatdoc output in the last line where the gnatdoc output files have been generated. The files used by ada2java are located in the gnatdoc/treecm directory.
Call
ada2java
tool providing the path to the treecm files directory.$ ada2java --gnatdoc-dir=obj/gnatdoc/trescm ...
Example:
$ ada2java -c mydir pack1.ads -b foo.bar
This results in the placement of the Java binding classes in the relative
directory mydir/foo/bar/
.
Note that the actual directory containing the generated Java classes will need
to be on the CLASSPATH
environment variable in order to successfully
run a Java application that uses the binding.
3.2. Compiling and Running the Generated Code¶
3.2.1. Issues with the Ada Generated Code¶
Two sets of Ada units need to be compiled – the original
packages and the generated ‘’glue’’ code. The Ada
glue depends on the ajis
project installed in the lib/gnat
directory of the GNAT-AJIS installation.
It is highly recommended that you use the project generation switches
-L
(for a shared library) or -M
(for an Ada main subprogram).
However, even if these switches handle most cases, you may
need to write your own build procedures to address more advanced usage.
In such a situation please note
that some compiler options may have an impact on the ajis library and thus
need to be taken into consideration:
-O2 -O3
If you compile with a high optimization level, you should deactivate strict aliasing using the compiler switch
-fno-strict-aliasing
.
-fstack-check
The stack checking mechanism is based on signals that are deactivated by the GNAT AJIS library, so this switch will have no effect and should not be used.
-fPIC
On Linux / Solaris, all the code has to be relocatable, which is specified through the
-fPIC
switch. If you are creating a shared library that integrates components compiled externally, you have to ensure that they have been compiled using the-fPIC
switch.
3.2.3. Compiling as an Ada Main Subprogram¶
If compiling the native code into a shared library is not practical, an alternative is to create an Ada main subprogram embedding a Java Virtual Machine.
ada2java
provides an easy way to generate a project and an Ada main
subprogram, through the -M
switch.
This switch takes the name of the
main as parameter and will generate an Ada main that will automatically create
a Java virtual machine, and then call a Java method defined as follows:
package <base_package>;
public class <main_name> {
public static void main (String [] args) {
}
}
This class (and thus the method implementation) has to be provided by the developer. If it is not present, the main subprogram will fail with an error at run time.
The generated main will look into the CLASSPATH environment variable to find the Java classes when initializing the Java virtual machine. So for example, if that you provide the following class:
package java_code;
import java_code.Test.Test_Package;
public class Main {
public static void main () {
Test_Package.Call_Something ();
}
}
using the following Ada API:
package Test is
procedure Call_Something;
end Test;
with the appropriate test.gpr
project referencing the Test
code,
you will be able to compile and run the code as follows:
$ ada2java test.ads -P test -b java_code -o ada -c java -M Main
$ gnatmake -P ada/main.gpr
$ CLASSPATH=`pwd`:`pwd`/java:$CLASSPATH
$ export CLASSPATH
$ javac java_code/Main.java
$ ada/obj/main
You can explicitly specify the name of the Java main class to use, through the
--main-class
switch, e.g.:
$ ada2java test.ads -P test -b java_code -o ada -c java \
> -M Main --main-class=some.main.My_Main
In this case, the Ada main will look for a main subprogram in
some.main.My_Main
, instead of java_code.Main
.
Note that you may need to define the LPATH
, LD_LIBRARY_PATH
or
PATH
environment variables so that the code can be compiled against
jvm.lib
or libjvm.a
, and then run with jvm.dll
or
libjvm.so
.
3.2.4. Compiling the Java Generated Classes¶
The Java application needs to load the library before any of the
Ada subprograms are invoked.
If you did not supply the -L
switch to ada2java
,
then you will need to do this explicitly; conventional style is to
invoke System.loadLibrary ("<library-name>")
in a static initializer in the main Java class.
This step is automated if you use the -L
switch, as described above.
Before running the Java code, you need to ensure that the
CLASSPATH
environment variable contains both the directory
of the generated Java code, and the JAR for the GNAT-AJIS-related
predefined classes. The latter archive exists as
$GNATAJIS_INSTALL_DIR/lib/ajis.jar
where
GNATAJIS_INSTALL_DIR
is the root directory for the GNAT-AJIS
installation.
3.3. Debugging an Ada / Java Application¶
The Ada code embedded in a Java application can be debugged through the standard GDB debugger. In order to do so, the following steps needs to be followed:
Start the Java Virtual Machine on the Java application containing the Ada code to debug.
Get the PID of the Java Virtual Machine. On Linux system, this is given by the ps command, on Windows, through the Task Manager.
Start GDB giving the Java Virtual Machine as argument.
Attach to the Java Virtual Machine, e.g.:
$> attach <pid>
You can then run a regular GDB session. For example, the following will set the GDB environment in Ada mode, break on all Ada exception, and then continue the application:
$> set lang ada $> break exceptions $> continue
If GDB fails to attach <pid>, returning the following error:
ptrace: Operation not permitted.
You can temporarily disable the ptrace protection by doing:
echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
To permanently disable it edit /etc/sysctl.d/10-ptrace.conf and change the line:
kernel.yama.ptrace_scope = 1
by
kernel.yama.ptrace_scope = 0
3.4. Debugging Ada memory allocations embedded in Java Applications¶
Adding GNATColl memory feature in embedded Ada code, simplify Ada memory problem investigation. (invalid memory access, double-deallocation & memory leak)
See memory_debugger or GNATbench/Memory* AJIS examples to see how to integrate GNATCOLL.Memory feature into your Java application.
You need to add to your sources:
gnatcoll.ads
gnatcoll-memory.ads
gnatcoll-memory.adb
getRSS.c
found in GNATCOLL sources and
s-memory.ads
memory_statistics.ads
memory_statistics.adb
found in AJIS memory_debugger example’s sources.
See Makefile & buildgpr.bat (Windows only) files of memory_debugger AJIS example to know how to modify the build process.
When using GNATCOLL.Memory package, it’s better to encapsulate all the Ada code in an encapsulated stand-alone library. When encapsuled, all ada allocations are done through GNATCOLL memory package.
To Enable the memory debugger, Memory_Statistics.Configure (Activate_Monitor=True) should be called.
It can be called from Java code through Memory_Statistics binded package.
Reset_Content_On_Free=True should be used to help invalid memory access detection,
Memory_Statistics.Dump_Memory_Statistics can be used to detect Ada memory leaks.
com.adacore.ajis.examples.memorystatistics.MemoryStatisticsViewer java class contains the source of a memory statistics dump viewer.
Relying on ‘nm’ & ‘addr2line’ GNU Binutils tools, this class displays in a sorted by size symbolic tree, the content of memory statistics dumps. It can be integrated in your Java application, or used as an external tool.
3.5. Pragma Annotate and ada2java¶
Pragma Annotate
(see GNAT Reference Manual)
has several uses in conjunction with the GNAT-AJIS tools,
each with the form:
pragma Annotate (AJIS, *AJIS_annotation_identifier* {, *argument*});
GNAT-AJIS annotation names are defined in the package AJIS.Annotations
,
which is a part of the ajis.gpr
project installed with GNAT-AJIS. You
need to have visibility on this package using a with
and possibly a
use
clause before being able to use these pragmas.
The following GNAT-AJIS annotation pragmas are supported:
Annotation_Renaming
– Dealing with Name ClashesAssume_Escaped
– Restrictions on Proxy-Owned Objects Passed to SubprogramsAttached
– Managing Attachment to Java ProxiesLocking
– Thread SafetyRename
– Dealing with Name ClashesResolve_Ambiguous_Expression
– Dealing with ambiguous operand in conversion errors