Gem #58: Ada / Java exception handling
by Quentin Ochem —AdaCore
Let's get started…
Let's consider the following API:
with AJIS.Annotations; use AJIS.Annotations; package API is function Compute (I : Integer) return Integer; type An_Operation is access function (I : Integer) return Integer; function Indirect_Compute (Data : Integer; Operation : An_Operation) return Integer; pragma Annotate (AJIS, Assume_Escaped, False, Indirect_Compute, “Operation”); end API;
with the following implementation:
package body API is function Compute (I : Integer) return Integer is Tmp : Natural := I; begin return Tmp + 1; end Compute; function Indirect_Compute (Data : Integer; Operation : An_Operation) return Integer is begin return Operation.all (Data); exception when Constraint_Error => return 0; end Indirect_Compute; end API;
The Compute subprogram uses a temporary variable of subtype Natural, and will raise a Constraint_Error if a negative value is passed for I. On the other hand, the Indirect_Compute subprogram catches all Constraint_Errors, and returns 0 if one occurs.
Generating the binding code
As with the previous Ada/Java interfacing Gems, the generation of the binding is straightforward:
ada2java api.ads –b test –o ada –c java –L my_lib –P api.gpr
Note that you need an api.gpr project file, defined as follows:
with “ajis”; project API is end API;
Writing the Java code
Let’s now play a bit with this code. As you can see, Java classes are generated for the Ada exceptions, such as test.Standard.Constraint_Error. These exceptions will end up as regular Java exceptions, which we can catch, for example, in the following code:
import test.API.API_Package; import test.Standard.Constraint_Error; public class My_Main { public static void main (String [] argv) { try { int x = API_Package.Compute (-1); } catch (Constraint_Error e) { System.out.println (e.getMessage ()); } } }
And just like that, we have an exception raised from Ada, transformed into a Java exception, and finally caught in a Java block. But let’s do something a little more ambitious. Let's raise an exception from Ada, have this exception pass through the Java frames, and then end up back in Ada:
import test.API.API_Package; import test.API.An_Operation; import test.Standard.Constraint_Error; public class My_Main { public static void main (String [] argv) { int x = API_Package.Indirect_Compute (-1, new An_Operation () { public int An_Operation_Body (int I) { return API_Package.Compute (I); } } ); } }
So, here we have a callback called from Ada, implemented in Java, that calls the Ada Compute procedure. Let’s see what happens in detail. First, Indirect_Compute is called by Java, goes into Ada, which then calls the subprogram passed as an access parameter, resulting in a call to Java. This Java function then calls the Ada Compute operation, which will raise an exception due to passing -1. So at the point of the exception, the call stack looks like this:
[1] Java : main [2] Ada : Indirect_Compute [3] Java : An_Operation_Body [4] Ada : Compute <- the exception is raised here
The exception first goes from [4] to [3], as in the previous example. In this case however, it is not caught, so it is propagated to the caller, [2], which happens to be Ada code again. Fortunately, the glue code generated between [2] and [3] is able to translate the exception back from Java to the original Ada exception, and continue the propagation. In this case, there’s an exception handler in [2], which handles Constraint_Error, and will catch the one initially raised at [4].
Compiling and running
Just as in the other Ada/Java interfacing Gems, the compile and run commands on Linux break down as follows:
gprbuild –p –P ada/my_lib.gpr LD_LIBRARY_PATH=`pwd`/ada/lib/:$LD_LIBRARY_PATH export LD_LIBRARY_PATH CLASSPATH=`pwd`:`pwd`/java:<your AJIS installation>/lib/ajis.jar:$CLASSPATH javac My_Main.java java My_Main