Previous Up Next

Chapter 13  Java Bindings


13.1  Introduction

This chapter provides an overview of the Java bindings for SIDL. Common aspects of the bindings, such as the mapping of SIDL data types to their Java counterparts, are presented in Section 13.2. Issues of concern to callers written in Java are addressed in the client-side discussion in Section 13.3, while issues for callees appear in the implementation-side discussion in Section 13.4.

13.2  Basics

This section summarizes basic features that are common to both client and implementation bindings. Subsection 13.2.1 describes conventions used to establish name spaces, while those associated with the generation of subroutines from methods are given in Subsection 13.2.2. The mapping of fundamental and key SIDL types is given in Subsection 13.2.3. Finally, casting between different types is discussed in Subsection 13.2.4.

13.2.1  Name space

SIDL’s object model is very similar to Java’s, and therefore maps easily into it. A SIDL object is treated almost exactly the same in Java as any other Java object, the only differences being that all data held by the object is private and all methods are public.

13.2.2  Method signatures

Since the bindings map well into Java language constructs, Java method signatures correspond very closely to those in the specification. Adapted from the Babel regression tests, the following is an example specification of a package called ExceptionTest that has a class named Fib with a getFib method declared as:

SIDL
int getFib(in int n, in int max_depth, in int max_value, in int depth) throws NegativeValueException, FibException;

The corresponding client-side, or stub, signature is:

Java
public native int getFib( int n, int max_depth, int max_value, int depth) throws ExceptionTest.FibException, ExceptionTest.NegativeValueException;

Note the one-to-one mapping in arguments and native exception types.

The corresponding implementation-side signature is:

Java
public int getFib_Impl ( /*in*/ int n, /*in*/ int max_depth, /*in*/ int max_value, /*in*/ int depth ) throws ExceptionTest.FibException, ExceptionTest.NegativeValueException, sidl.RuntimeException.Wrapper

Once again, there is a one-to-one mapping of arguments and the two specified exceptions. However, the implementation side includes the (implicit) sidl.RuntimeException’s Wrapper class (since RuntimeException is an interface. For more on the reasoning behind this, refer to Subsection 13.2.3.

Out and inout arguments

Unlike languages such as C/C++, Java does not support pointers. As a result, out and inout arguments are handled — for each SIDL type and class — through a static inner Holder class. The class can hold a single variable or object of the correct type. Built-in functions get() and set() are provided for accessing the data.

13.2.3  Data types


Table 13.1: SIDL to Java Type Mappings
SIDL TYPEJAVA TYPE
intint
longlong
floatfloat
doubledouble
boolboolean
charchar
stringString
fcomplexFloatComplex
dcomplexDoubleComplex
enumEnum
opaquelong
interfaceinterface
classclass
arraytype.Array

Most SIDL types map directly into Java as shown in Table 13.1. The remainder of this subsection focuses specifically on interfaces, abstract classes, exceptions (in general), enumerations, and arrays.

interfaces and Abstract Classes

As a result of the fact that Java interfaces cannot hold data, an additional layer was added to the Java bindings for internal processing needs within the implementation of the SIDL object model — such as throwing an (interface) exception. For this reason, wrappers were created for interfaces and abstract classes. Called Wrapper, these classes hold the interface IOR pointer, inherit from gov.llnl.babel.BaseClass, and implement the outer interface. Therefore, all interface and gov.llnl.babel.BaseClass methods — such as _cast2 and isType — can be called on the wrapper object. In addition, the wrapper object is returned when an interface is retrieved from an array. The client is not usually affected in this case. The wrapper class is also used for methods that take or return interface arguments and when an exception implemented as an interface is caught. In the latter case, the developer must be sure to catch the wrapper class. Examples appear later in this chapter.

Exceptions

Although SIDL exceptions may be specified as interfaces while Java exceptions are always classes, SIDL exceptions are basically caught and thrown in the native Java manner. A critical difference, as mentioned in the discussion on interface data types, is that a SIDL exception interface’s wrapper object must be thrown. While similar from a developer standpoint in that SIDL exceptions are mapped to Java exceptions, the converse is not true. That is, there is no mapping of all Java exceptions to SIDL exceptions.

If an exception is defined in SIDL, Babel will generate code for it. The only difference between native Java and SIDL exceptions is that the SIDL exception constructor cannot take a String argument. Instead, the message must be set with the inherited setNote method and retrieved with getNote. This is important because SIDL exceptions inherit from the Java Exception class. The Java compiler will not give an error if getMessage is called; however, the message returned will not be from SIDL.

Another problem is that regular Java exceptions cannot be passed through the SIDL middleware. Since it is not possible to throw normal, non-SIDL exceptions from a SIDL function implemented in Java, the Java compiler will throw an error if an attempt to do so is made. There are some exceptions. For example, Java runtime exceptions such as ArrayIndexOutOfBoundsException can be thrown. In this case, an error message and stack trace are printed to stderr. In addition, the method returns 0; values of any out or inout arguments are set to NULL; and the program proceeds.

For more information on catching and throwing exceptions, including examples, refer to Subsections 13.3.7 and 13.4.4.

Enumerations

Enumerations are implemented as final static ints in their own Java class and, as such, are accessed just like variables in that class. Recall the specification of a car enumeration type, from Section 6.3 and repeated below, defines three constants: porsche, ford, and mercedes.

Java
enum car { porsche = 911, ford = 150, mercedes = 550 };

The value of the porsche constant, for example, can be accessed through enums.car.porsche.

Arrays

As discussed in Section 6.4, SIDL supports both normal and raw arrays (i. e., r-arrays). Normal SIDL arrays can be used by any supported language; whereas, r-arrays are restricted to numeric types. This subsection discusses both within the context of Java bindings.

Every object and type defined in SIDL has a corresponding array to hold elements of that type. In the case of Java bindings, this means the entire SIDL array API is available with a few exceptions that have no real use in the language. More specifically, ensure(), borrow(), and first() are not supported in the bindings. Unlike with most of the other language bindings, explicit array deletion should be done using the destroy() array function. Refer to Subsection 6.4 for more information on the API.

More to the point are the specifics of the Java implementation — which provides a wider variety of options for constructing arrays than other bindings. Each SIDL type and class includes a static inner class named Array. This is the main Array class and, in order to support up to 7 dimensional arrays, every method takes either 7 array indices or an array of indices. For example, in order to get the element (2,3) from a 2 dimensional array, arry._get(2,3,0,0,0,0,0) would be used. Since typing all those zeros can get a little tedious, a set of array subclasses have also been implemented with one subclass per supported dimension. So, given an Array2 instead of an Array, arry2._get(2,3) could be used to get the element (2,3) instead.

These numbered array subclasses improve on the array API usability somewhat, but they do have a side effect. In order to avoid conflicts between the array superclass and the numbered array subclass functions, all other basic array methods found in the Array superclass are preceded by an underscore ’_’. For example, arry._dim() returns an array’s dimensionality. Since numbered arrays all inherit these methods, they can be used as well. In the dimensionality example, arry2._dim() could also be used — though the answer should be obvious.

Furthermore, there is another underscore rule for arrays in Java. All numbered arrays have two get and two set functions. The _get and _set functions are the same in Array and all the Array# subclasses in that they simply pass the arguments of the _get call down to the underlying implementation. However, the underscore-less get and set methods do bounds checking in Java before calling the underlying implementation. If a problem is detected, they throw ArrayIndexOutOfBoundsException.

Because numbered arrays are subclasses of Array, an Array# can be Java cast to an Array, if necessary. However, some functions return an Array. In order to convert an Array to the correctly numbered array, a function in Array, called _dcast(), can be used by simply invoking _dcast() on the object. For example, given a one-dimensional array of type foo.bar called arry that is represented by the Java class Array, the correctly numbered array type can be retrieved as follows:

Java
foo.bar.Array1 arry1 = arry._dcast();

After this cast, two references are now available to the same array; namely, arry and arry1.

Finally, the Java array constructors are slightly different than in other languages. The constructor definition for Array is:

Java
public Array(int dim, int[] lower, int[] upper, boolean isRow)

This constructor creates an array of dimension dim. It takes two arrays of integers to define the lower and upper bounds of each dimension in the array. If lower or upper has fewer elements than there are dimensions in the array, or any element in lower is larger than the corresponding element in upper, the constructor will throw an exception. Finally, the constructor takes a boolean isRow. If isRow is true, a SIDL array will be created in row-major order; if false, a column-major order array will be created.

Constructors for numbered arrays are simpler. The constructor for a two-dimensional array is:

Java
public Array2( int l0, int l1, int u0, int u1, boolean isRow)

Since the dimensionality is known, the dimension argument was dropped. In addition, it is no longer necessary to create arrays of bounds to pass into the constructor; instead, l0 and l1 are the lower bounds and u0 and u1 the upper. The choice between column- and row-major orders is obviously still necessary.

For arrays with all lower bounds of zero, an even simpler constructor is available. Its signature is:

Java
public Array2( int s0, int s1, boolean isRow)

Another alternate construction method for SIDL arrays is present in numbered arrays. The following constructor takes a two-dimensional Java array, and copies it into a 2-dimensional SIDL array:

Java
public Array2(foo.bar[][] array, boolean isRow)

Alternatively, arrays can be constructed as copies of existing arrays through two additional built-in methods. An existing numbered SIDL array of the correct dimension can be set to the same contents of a Java array with the fromArray method. The same arguments as the constructor above are used but nothing is returned. Conversely, a SIDL array can be copied into a Java array through the toArray numbered array function. The function takes no arguments but returns a new Java array containing copies of the SIDL array’s elements.

13.2.4  Type casting

There are two issues associated with casting of types in the Java bindings. The simplest is the casting of SIDL objects implemented in Java. The second involves the casting of SIDL interfaces which, as discussed in Subsection 13.2.3, is a little more involved. This subsection describes the normal, object casting process before that of the Java equivalents of SIDL interfaces.

In some cases it is necessary to cast the internal representation of an object as well as the Java object. (For example, getting an object from a SIDL array of superclass objects.) In these cases a Java cast is insufficient. Therefore two built-in casting functions have been provided: _cast() and _cast2(). The static _cast(object) function returns a new Java object based on the object argument. For example, foo.bar newobj = (foo.bar) foo.bar._cast(oldobj) will cast oldobj, an object of type sidl.BaseClass, to foo.bar. If this is an invalid cast, _cast will return null.

The _cast2(‘‘ClassName’’) method, on the other hand, casts an object to a named type (i. e., ClassName). It performs basically the same function as _cast, but the form is object._cast2(‘‘ClassName’’), where ClassName must be a fully qualified name. If the cast is invalid, or a class of that name cannot be found, null is returned.

Both functions return a sidl.BaseClass which must then be Java casted to the correct Java class type. They both also create a new Java object that owns a new reference to the IOR object. Although you never have to worry about reference counting in Java, it is important to remember that casting leaves two valid objects.

As mentioned in Subsection 13.2.3, SIDL interfaces are mapped to wrapper classes that inherit from an interface. As a result, they can be Java cast to their ancestor interfaces but must be Babel cast to any classes. In the following example, Subclass implements SuperInterface:

Java
SuperInterface.Array1 arry = new SuperInterface.Array1(5, true); SubClass obj = new SubClass(); arry.set(0, (SuperInterface)obj); obj = null; SuperInterface temp = arry.get(0); obj = (SubClass) temp; //INCORRECT Will throw ClassCastException obj = (SubClass) SubClass._cast((SuperInterface.Wrapper)temp); //CORRECT

Finally, in some cases, as when the interface is retrieved from an array, Java casting the interface is not necessary before Babel casting it; however, that is not true in general. The following is an example of casting an interface in a Java implementation:

Java
public objarg.SubClass toClass_Impl (/*in*/ objarg.Iface ifcy ) { // DO-NOT-DELETE splicer.begin(objarg.SubClass.toClass) objarg.SubClass ret = (objarg.SubClass) ((objarg.Iface.Wrapper)ifcy)._cast2("objarg.SubClass"); return ret; // DO-NOT-DELETE splicer.end(objarg.SubClass.toClass) }

13.3  Client-side

This section summarizes aspects of generating and using the Java bindings associated with software wrapped with Babel’s language interoperability middleware. The bindings generation process is presented before discussing required environment variables. The manner in which SIDL-specified types are imported into Java is then given. Object management and invocation of static and overloaded methods are also summarized. The process of catching exceptions is then discussed. Finally, the processes for enabling and disabling implementation-specific pre- and post-method instrumentation — referred to as “hooks” — are illustrated.

13.3.1  Bindings generation

Java stubs (i. e., code to support Java clients for a set of SIDL classes or interfaces) are created by invoking Babel as follows1:

% babel --exclude-external --client=Java file.sidl

or simply

% babel -E -cJava file.sidl

This will create a plethora of files, including a directory named file, which contains the Java client classes. Files ending in _IOR.h and _IOR.c implement the Intermediate Object Representation (IOR). Files ending with _jniStub.c are the Java Native Interface (JNI) stubs. The use of “jni” in the filename reflects the fact that the JNI is used to communicate between Java and the IOR. The remaining header files include the external Java API used by Java clients.

The stub files — whose names end with _jniStub.c — must be compiled and linked against the SIDL runtime library and an implementation. The resulting library needs to be referenced in a .scl file listed in the SIDL_DLL_PATH environment variable so the Babel runtime library loader can find it. Also, the current directory needs to be in the CLASSPATH environment variable so Java can find the file and sidl directories containing the client-side Java components. More information on environmental settings is given in Subsection 13.3.2.

13.3.2  Environment variables

Four environment variables are associated with running Java bindings. The first three, described below, identify path-related directories that must be set properly for various tools. It is assumed Babel was already installed in directories rooted at $PREFIX. The last variable is used to pass options to the implementation-side.

CLASSPATH.
Java uses the CLASSPATH environment variable to find .class files. While not specific to Babel, this colon-separated list of directories must include at least three directories. The first is the one containing the implementation-side .class files. The second is the directory containing sidl_$VERSION.jar, where $VERSION is the version of your installed Babel, which is likely to be the SIDL jar file. The latter directory should be $PREFIX/lib. Finally, CLASSPATH must include $PREFIX/runtime/java.
LD_LIBRARY_PATH (or LIBPATH on AIX).
Many systems require LD_LIBRARY_PATH environment variable contain $PREFIX/lib. However, the system-specific environment variable should be used. AIX, for example, uses LIBPATH. The goal is to include $PREFIX/lib in the search path used to locate shared/dynamic link libraries.
SIDL_DLL_PATH.
The SIDL_DLL_PATH environment variable is typically required to contain the path to the directory holding the shared/dynamic link library containing the implementation of SIDL objects. This is a semicolon-separated path variable.
BABEL_JVM_FLAGS.
Consisting of a semicolon-separated list of command line options, BABEL_JVM_FLAGS passes them on to the implementation. For instance, -Xcheck:jni can be useful since it results in the JVM validating parameters and environment data prior to JNI requests. The following command illustrates additional useful options:
shell
BABEL_JVM_FLAGS="-verbose:gc;-Xmx500m"

WARNING: Not including all the necessary files in the SIDL_DLL_PATH and LD_LIBRARY_PATH can crash the JVM in unexpected ways. Babel tries to generate helpful error messages, but sometimes the JVM crashes due to missing files with the resulting output not being very helpful. If the JVM crashes, SIDL_DLL_PATH and LD_LIBRARY_PATH should be checked to ensure they point to the necessary files.

13.3.3  Imports

Importing SIDL packages and classes is also similar to the native Java approach. Babel generates Java code in subdirectories to organize packages and classes in the same manner as done in Java. For example, assume the presence of a specification of a package called test that contains two classes: HelloWorld and GoodbyeWorld. Running babel -cJava test.sidl in the directory named babelcode will result in the generation of a new subdirectory called test containing the following two files: HelloWorld.java and GoodbyeWorld.java. The resulting classes will be accessible from any Java program that imports them when babelcode is in the CLASSPATH. In this example, the import statement for the first class is:

Java
import test.HelloWorld;

13.3.4  Object management

SIDL-specified objects are managed through explicit creation but explicit reference counting is basically unnecessary. Thanks to the straightforward mapping between SIDL and Java types, SIDL-specified concrete classes can be instantiated through Java’s new. For example, given a package test that includes the class HelloWorld, the following code snippet illustrates the creation of an object as well as its use:

Java
import test.HelloWorld; public static main(String args[]) { HelloWorld hi = new HelloWorld(); hi.printMsg(); }

Writing the fully qualified class name would also have sufficed as in:

Java
public static main(String args[]) { test.HelloWorld hi = new test.HelloWorld(); hi.printMsg(); }

WARNING: Although addRef and deleteRef exist in Java, they should not be used because Java decrements the reference count itself when it garbage collects a SIDL object.

13.3.5  Static methods

13.3.6  Overloaded methods

Using the overload_sample.sidl file from Section 6.7 as an example, recall that three versions of the getValue method are specified. The first signature takes no arguments, the second takes an integer, and the third a boolean. The code snippet below illustrates object creation, and method invocation for each of the overloaded methods.

Java
boolean b1, bresult; int i1, iresult, nresult; Overload.Sample t = new Overload.Sample(); nresult = t.getValue(); bresult = t.getValue(b1); iresult = t.getValue(i1);

13.3.7  Exception catching

Recall Subsection 13.2.3 discussed issues associated with the fact that SIDL supports exception interfaces while Java does not. In the following example, derived from regression tests, a getFibi method takes an integer argument and can throw one of two exceptions specified as SIDL exception interfaces: NegativeValueException and TooDeepException.

Java
try { fib.getFibi(-1); } catch (NegativeValueException.Wrapper ex) { System.err.println(ex.getNote()); } catch (TooDeepException.Wrapper ex) { System.err.println(ex.getNote()); } catch (java.lang.Exception ex) { if (((sidl.BaseInterface)ex).isType("sidl.SIDLException")) { System.err.println("Unexpected SIDL Exception thrown"); } else { System.err.println("Unexpected and unknown exception thrown"); } }

Since the two exception types are specified as interfaces, the code to trap each must reference their Wrapper classes. Hence, the use of each class’s fully qualified name in the catch clauses.

The example also illustrates another option that is generally available for distinguishing between exception types. That is, the body of the final catch includes a call to the isType() method, which is used to check the exception against a named type. In this example, however, SIDL can cast between the two interfaces, so isType() would return true regardless of the the type of the exception instance.

13.3.8  Hooks execution

If a given component supports pre- and post-method invocation instrumentation, also known as “hooks”, their execution can be enabled or disabled at runtime through the built-in _set_hooks method. For example, given the following SIDL specification:

SIDL
package hooks version 1.0 { class Basics { /** * Basic illustration of hooks for static methods. */ static int aStaticMeth(in int i, out int o, inout int io); /** * Basic illustration of hooks for static methods. */ int aNonStaticMeth(in int i, out int o, inout int io); } }

which has a single static function and a member function for the Basics class, the processes for enabling and disabling execution of the implementation-specific hooks are:

Java
try { hooks.Basics obj = new hooks.Basics(); /* * Enable hooks execution (enabled by default) * ...for static methods */ hooks.Basics._set_hooks_static(true); /* * ...for non-static methods */ obj._set_hooks(true); /* * ...do something important... */ /* * Disable hooks execution * ...for static methods */ hooks.Basics._set_hooks_static(false); /* * ...for non-static methods */ obj._set_hooks(false); /* * ...do something important... */ } catch (Throwable ex) { ex.printStackTrace(); }

It is important to keep in mind that the _set_hooks_static method must be used to enable/disable invocation of hooks for static methods and the _set_hooks method must be used for those of non-static methods. Also, Babel does not provide client access to the _pre and _post methods; therefore, they cannot be invoked directly. More information on the instrumentation process is provided in Subsection 13.4.5.

13.3.9  Contract enforcement

Interface contracts specify the expected behaviors of callers (or clients) and callees (or servers) of methods defined for interfaces and classes. Once specified, contracts are optionally enforced at runtime, through checks automatically integrated into the middleware generated by the Babel compiler. This section provides an example of a specification and code snippets for performing basic, traditional contract enforcement — introduced in Section 6.5 — in a Java client.

A SIDL specification, including preconditions and postconditions, for calculating the sum of two vectors is given below. (Refer to Section 6.5 for an introduction to the contract syntax.) According to the preconditions, all callers are expected to provide two one-dimensional, SIDL arrays of the same size as arguments. The postconditions specify that all implementations are expected to return a non-null, one-dimensional array of the same size (as the first SIDL array), assuming the preconditions are satisfied.

SIDL
package vect version 1.0 { class Utils { /* ... */ /** * Return the sum of the specified vectors. */ static array<double> vuSum(in array<double> u, in array<double> v) throws sidl.PreViolation, sidl.PostViolation; require not_null_u: u != null; u_is_1d : dimen(u) == 1; not_null_v: v != null; v_is_1d : dimen(v) == 1; same_size: size(u) == size(v); ensure no_side_effects : is pure; result_not_null: result != null; result_is_1d : dimen(result) == 1; result_correct_size: size(result) == size(u); } /* ... */ }

An example of a Java client invoking the method is given below. The code snippet illustrates declaring and creating the arrays; enabling full contract enforcement (i. e., checking all contract clauses); executing vuSum; and handling contract violation exceptions is given below.

Java
import sidl.ContractClass; import sidl.Double.Array1; import sidl.EnfPolicy; import sidl.PostViolation; import sidl.PreViolation; import sidl.SIDLException; import vect.Utils; /* ... */ Array1 u = new Array1(0, S_MAX_SIZE-1, false); Array1 v = new Array1(0, S_MAX_SIZE-1, false); /* Initialize u and v. */ /* Enable FULL contract enforcement. */ try { d_policy.setEnforceAll(sidl.ContractClass.ALLCLASSES, true); } catch (SIDLException exc) { /* Handle the exception */ } /* Do something meaningful before execute method. */ try { Array1 x = vect.Utils.vuSum(u, v); if (x != null) { /* Do something meaningful with the result, x. */ } } catch (PreViolation preExc) { /* Handle the precondition violation, preExc. */ } catch (PostViolation postExc) { /* Handle the postcondition violation, postExc. */ } catch (Exception exc) { /* Handle the undifferentiated exception, exc. */ }

Alternative enforcement options can be set, as described in Section 6.5, through the two basic helper methods: setEnforceAll and setEnforceNone. The code snippet below shows the Java calls associated with the traditional options of enabling only precondition enforcement, enabling postcondition enforcement, or completely disabling contract enforcement.

Java
import sidl.ContractClass; import sidl.EnfPolicy; /* ... */ policy = new sidl.EnfPolicy(); /* * Enable only precondition contract enforcement. * (Useful when only need to ensure callers comply with contract.) */ try { policy.setEnforceAll(sidl.ContractClass.PRECONDS, false); } catch (SIDLException exc) { /* Handle the exception */ } /* * Enable only postcondition contract enforcement. * (Useful when only need to ensure implementation(s) comply with contract.) */ try { policy.setEnforceAll(sidl.ContractClass.POSTCONDS, false); } catch (SIDLException exc) { /* Handle the exception */ } /* * Disable contract enforcement. * (Should only be used when have confidence in caller AND implementation.) */ try { policy.setEnforceNone(false); } catch (SIDLException exc) { /* Handle the exception */ }

This section illustrates the basic interfaces and processes for traditional interface contract enforcement for a Java client. Additional enforcement policy options and methods as well as more information regarding the specification and enforcement of contracts can be found in Chapter 21.

13.4  Implementation-side

This section summarizes aspects of generating and wrapping software written in Java. The bindings generation and basic implementation processes are presented first. Accessing private data is then discussed before illustrating the process of throwing exceptions. Finally, the results of generating implementations with pre- and post-method “hooks” are shown.

13.4.1  Bindings generation

Babel supports calls to SIDL classes implemented in Java. These classes obey the same rules described in Subsection 13.3.1 for client-side Java classes, except that in this case the file, class, and method names all end in _Impl. Implementation of a set of SIDL classes in Java first requires the generation of the necessary bindings. This is accomplished by invoking Babel as follows:

% babel --exclude-external --server=Java file.sidl

or simply

% babel -E -sJava file.sidl

The directory structure that results from this command is the same as that produced on the client-side except there are many more files. Given the SIDL file is named “file.sidl”, as above, a file subdirectory is created and “Impl” files (i. e., those ending in _Impl.java) are automatically generated in that subdirectory. These Java “Impl” files are supposed to contain implementation details. Refer to Subsection 13.4.2 for details.

In the current directory there are also new files that end in _jniSkel.c. These files are equivalent to the client-side _jniStub.c. In fact, all client-side files are generated from this call as well to allow for calling methods on the current object in the Java “Impl” file. Consequently, they can safely be ignored.

13.4.2  Bindings implementation

Implementation details must be added to the “Impl” files generated in Subsection 13.4.1. Changes to these files must be made between code splicer pairs to ensure their retention in subsequent invocations of Babel. Code splicing is a technique for preserving hand-edited code between multiple invocations of Babel. This allows a developer to refine the implementation without losing previous implementation details. Hence, code between splicer pairs will be retained by subsequent invocations of Babel; whereas, code outside splicer pairs will not.

Another interesting fact of the implementation-side is that it inherits from the client-side Java class. This allows calls to local methods directly. Take this recursive Fibonacci function implementation, for example:

Java
class Fib_Impl extends Fib { public int getFib_Impl(int x) { // DO-NOT-DELETE splicer.begin(ExceptionTest.Fib.getFib) if(x >= 2) { return getFib(x-1) + getFib(x-2); } else { return 1; } // DO-NOT-DELETE splicer.end(ExceptionTest.Fib.getFib) } }

The client-side class name is Fib and, therefore, the implementation-side class is Fib_Impl. The same relation holds for the getFib method. Note that getFib, the client-side method, can be called directly. A call like this goes through Babel glue code, as it should. That is, calls directly to _Impl methods should never be made since they break the object model for the current class and will not work on different objects. The reason for this situation is that, by making local calls, within fib_Impl for example, any inheritance information stored in the middleware is lost. It also means implementation-side object inheritance from non-SIDL Java classes is impossible. In fact, since no splicer blocks are available for inheritance, implementing interfaces on the implementation-side is also not supported since having the implementation-side inherit from non-SIDL classes is probably not a good idea.

13.4.3  Private data

Any variables declared in the implementation source file will, by virtue of Babel’s encapsulation, be private. The data can be global to the class — as in static variables declared within the _includes splicer block at the top of the class’s _Impl.java file — or “local” to an instance. In the former case, special initialization procedures can be added to the built-in _load() method, which is guaranteed to be called exactly once per class — before any user-defined methods can even be invoked. The latter case relies on the class-specific name space automatically generated in the implementation’s header file.

13.4.4  Exception throwing

Recall Subsection 13.2.3 discussed issues associated with SIDL support for exception interfaces versus Java’s requirement that all exceptions be classes. Below is an extension of the example in Subsection 13.3.7 involving getFibi, which could throw one of the following two interface exceptions: NegativeValueException and TooDeepException.

Java
public int getFibi_Impl ( /*in*/ int n) throws NegativeValueException.Wrapper, TooDeepException.Wrapper { // DO-NOT-DELETE splicer.begin(ExceptionTest.Fib.getFibi) if (n < 0) { FibException fex = new FibException(); NegativeValueException.Wrapper neg = (NegativeValueException.Wrapper) NegativeValueException.Wrapper._cast(fex); neg.setNote("n negative"); throw neg; } // .... Do Fibonacci stuff .... // DO-NOT-DELETE splicer.end(ExceptionTest.Fib.getFibi) }

Notice that the interface exceptions and their Wrappers cannot be instantiated directly. Instead, a FibException object is created then cast to the appropriate exception interface type. As in Subsection 13.3.7, the wrapper class’s full name is required during the cast operation. Finally, the example illustrates the use of setNote to add the message to the exception being thrown — which is necessary since the note cannot be passed to the exception’s constructor.

13.4.5  Hooks implementation

As discussed in Subsection 13.3.8, when hooks execution is enabled, implementation-specific instrumentation is executed. Using the --generate-hooks option on the Babel command line when generating implementation-side bindings results in the automatic generation of a _pre and _post method for every static and non-static method associated with each class in the specification. For the aStaticMethod specified in Subsection 13.3.8, the generated _pre method implementation is:

Java
public static void aStaticMeth_pre_Impl ( int i, int io ) throws sidl.RuntimeException.Wrapper { // DO-NOT-DELETE splicer.begin(hooks.Basics.aStaticMeth_pre) /* * Add instrumentation here to be executed immediately prior * to dispatch to aStaticMeth(). */ // DO-NOT-DELETE splicer.end(hooks.Basics.aStaticMeth_pre) }

while that of the _post method is:

Java
public static void aStaticMeth_post_Impl ( int i, int o, int io, int _retval ) throws sidl.RuntimeException.Wrapper { // DO-NOT-DELETE splicer.begin(hooks.Basics.aStaticMeth_post) /* * Add instrumentation here to be executed immediately after * return from dispatch to aStaticMeth(). */ // DO-NOT-DELETE splicer.end(hooks.Basics.aStaticMeth_post) }

Per the normal implementation process, the desired instrumentation should be added within the splicer blocks of aStaticMethod_pre and aStaticMethod_post. As stated in the comments within those blocks, aStaticMethod_pre will be executed immediately prior to dispatch to aStaticMethod when the latter is invoked by a client. Assuming no exceptions are encountered, aStaticMethod_post is executed immediately upon return from aStaticMethod.


1
For information on additional command line options, refer to Section 4.2.

Previous Up Next