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

About the Author

Quentin Ochem has a software engineering background, specialized in software development for critical applications. He has over 10 years of experience in Ada development. He works today as a technical account manager for AdaCore, following projects related to avionics, railroad, space and defense industries. He also teaches the avionics standard DO-178B course at the EPITA University in Paris.