next node: Subsystem JavaVM,
prev node: Tk,
up to node: Subsystem Tools


Subsystem Java

This is the technical documentation for the JavaBind feature (version 1.1a).

JavaBind provides a type-safe interface to Java software in Opal. For this purpose, a so-called binding compiler (command javabind) automatically produces Opal structures which connect to Java via the JNI (Java Native Interface). The Opal library-package Java supports the code produced by the binding compiler and adds some generic functionality such as catching exceptions and testing for null objects.

For some examples of using JavaBind, see `/usr/ocs/examples/Java'.

Mapping Java to Opal

JavaBind maps Java classes to Opal in a systematic way, as described in the following sections. The intention is that it should not be necessary for a JavaBind user to actually look at the generated Opal bindings; the documentation for the Java classes together with knowledge of their representation in Opal should suffice.

Representing Subclassing

For any Java class C we have an Opal sort of the same name C with conversion functions mapping upwards and downwards along the class hierarchy. The conversion functions correspond to casting in Java, (C)Obj, and are provided for any pair of classes which belong to the same path in the inheritance tree. As in Java, casting downwards along the hierarchy is a partial operation whose definedness can be tested with the function C?, which corresponds to the instanceof primitive.

Consider the following Java classes (to illustrate the principle, the inheritance from Object is made explicit):

class A extends Object {...}
class B extends A {...} 

These classes and their inheritance relations are represented in Opal by the following sorts, conversions and tests:

SORT A
FUN Object   : A       -> Object
FUN A        : Object  -> A
FUN A?       : Object  -> bool

SORT B
FUN A        : B       -> A
FUN Object   : B       -> Object
FUN B        : Object  -> B
FUN B        : A       -> B
FUN B?       : Object  -> bool
FUN B?       : A       -> bool

Representing Fields

Public fields of objects are represented by commands which allow their value to be retrieved and defined. Consider the class definition

public class A {public B field; ... }

field is represented in Opal as follows:

FUN field     : A -> com[B]
FUN :=_field  : A ** B -> com[void]

Representing Methods

Public methods of objects are represented by commands. Consider the class definition

public class A {public B method(B1 a1, ..., Bn an) { ... } }

method is represented in Opal as follows:

FUN method : A ** B1 ** ... ** Bn -> com[B]

Note that the method overloading of Java is perfectly matched by Opal's function overloading.

Inheritance and Overwriting

Inheritance of fields and methods of superclasses is modelled by repeating their declarations in the subclass. Consider:

public class A { public void method1 () { ... }
                 public void method2 () { ... } }

public class B extends A
               { public void method2 () { ... } }

This is interfaced by the Opal declarations:

FUN method1 : A -> com[void]
FUN method2 : A -> com[void]

FUN method1 : B -> com[void]
FUN method2 : B -> com[void]

Let ObjB be an object of type B. Then method1(ObjB) invokes the method as it is defined in the superclass A, and method2(ObjB) the overwritten version of the method in B.

Dynamic dispatching is dealt with much as in Java. Having an object ObjA which is known in the context to be an instance of class A but actually has class B and then calling method2(ObjA) invokes the overwritten version from class B. Henceforth, method2(A(ObjB)) does not invoke the definition of method2 from class A, but from B.

A corresponding concept for Java's super primitive, which allows an overwritten method of a superclass to be invoked, is not provided. In Java, super is only allowed inside the same class definition where the overwriting happens. Such a context is not present in Opal's view on Java, since classes are always accessed from the outside.

Static Fields and Methods

Static fields and methods are treated in a similar way to normal ones. Consider:

public class A {
   static public B field;
   static public B method(B1 a1, ..., Bn an) { ... }
}

This is represented in Opal as follows:

FUN field  : com[B]
FUN method : B1 ** ... ** Bn -> com[B]

Interfaces

Interfaces are treated in a similar way to classes.

Exceptions

Exceptions thrown by Java methods lead to a failure of the corresponding command execution. The structure JavaCatch ( JavaCatch) provides a command combinator to check for such failures and also to retrieve the object thrown by the exception:

(... commands ...) catch (\\Throwable. ...)

Primitive Types, Strings and Arrays

The primitive Java types, strings and arrays are automatically mapped to corresponding Opal types. Consider:

public class A {
   static public int[] method(int k, String[] arr[]) { ... }
}

This is represented in Opal as:

FUN method : int ** array[array[denotation]] -> array[int]

The mappings are as follows:

boolean
is represented as bool'BOOL
byte
is represented as byte'Byte ( Byte), which is just a wrapper around int'Int (Opal currently has no direct equivalent to Java's byte type).
short
is represented as short'Short ( Short), which is just a wrapper around int'Int (Opal currently has no direct equivalent to Java's short type).
int
is represented as int'Int. Unfortunately, Opal integers have only 31-bit accuracy, so the highest bit is lost. We currently prefer this pragmatic treatement instead of the inconvenience of using a non-standard Opal type for integers.
long
is represented as long'Long ( Long), which is just a wrapper around denotation'DENOTATION (Opal currently has no direct equivalent to Java's long type, and there is no type which can simulate 64-bit integers - thus they are represented in their textual form).
float
is represented as sreal'SmallReal. Unfortunately, in Opal small reals have only 31-bit accuracy, so the highest bit is lost.
double
is represented as real'Real.
String
is represented as denotation'DENOTATION. Null strings are represented by empty denotations; a special test function allows one to observe whether a denotation has been created from null ( JavaNull).
C[]
is represented as array'Array. Null arrays are represented by empty arrays; a special test function allows one to observe whether an array has been created from null ( JavaNullArray).

Mapping Classes to Structures

In Java, arbitrary cyclic dependencies between classes - even across package boundaries - can appear. Opal only allows acyclic import relations. This problem is dealt with by actually generating two structures for each Java class: the first one contains only the sorts and conversions, the second the fields and methods. Consider:

public class A { public B foo () { ... } }
puplic class B { public A bar () { ... } }

Schematically, the following structures are generated to interface these classes:

SIGNATURE A_SORT
  SORT A
  ...
SIGNATURE B_SORT    
  SORT B
  ...
SIGNATURE A
  IMPORT A_SORT COMPLETELY
  IMPORT B_SORT ONLY B
  FUN foo : A -> com[B]
SIGNATURE B
  IMPORT B_SORT COMPLETELY
  IMPORT A_SORT ONLY A
  FUN bar : B -> com[A]

In virtually all cases, the user does not need to know about this treatment, since the structure containing the fields and methods also re-exports the sorts and conversions.

Operation of the javabind tool

The javabind tool works on an input file which describes the bindings to be generated and names the subsystem in which they should be placed. Here is an examle of a javabind file:

// name of the subsystem to generate
system "JavaLib" 
  // bind a class
  bind "java.io.BufferedInputStream"

Running javabind on this file produces the following output:

javabind version 1.1a
class java.io.InputStream
  indirectly referred (requesting only sorts and casts)
  bounded in java_io_InputStream
  java_io_InputStream_SORT reused from /home/uebb/opal/2.3a/lib/opal_java
class java.io.BufferedInputStream
  bounded in java_io_BufferedInputStream
class java.io.FilterInputStream
  indirectly referred (requesting only sorts and casts)
  bounded in java_io_FilterInputStream
class java.lang.String
  indirectly referred (requesting only sorts and casts)
  bounded in java_lang_String
  java_lang_String_SORT reused from /home/uebb/opal/2.3a/lib/opal_java
interface java.io.Serializable
  indirectly referred (requesting only sorts and casts)
  bounded in java_io_Serializable
  java_io_Serializable_SORT reused from /home/uebb/opal/2.3a/lib/opal_java
class java.lang.Class
  indirectly referred (requesting only sorts and casts)
  bounded in java_lang_Class
  java_lang_Class_SORT reused from /home/uebb/opal/2.3a/lib/opal_java
  java_lang_Class reused from /home/uebb/opal/2.3a/lib/opal_java
class java.lang.Object
  indirectly referred (requesting only sorts and casts)
  bounded in java_lang_Object
  java_lang_Object_SORT reused from /home/uebb/opal/2.3a/lib/opal_java
  java_lang_Object reused from /home/uebb/opal/2.3a/lib/opal_java

As seen from the output, javabind not only generates bindings for classes which are directly requested, but also for classes which are indirectly referenced from the requested ones. These are superclasses and interfaces of the requested classes, as well as classes referenced from the member signatures.

In order to minimize the amount of bindings, for indirect referred classes only the structure containing the sorts and conversions is generated (such as java_io_InputStream_SORT). Moreover, bindings are reused from existing libraries of Java bindings, supporting the incremental construction of richer binding sets from existing ones. A default set of bindings is available in the basic Opal-Java library. This set is constructed by the javabind file:

system "JavaDefault
  bind "java.lang.Object"
  bind "java.lang.Class"
  bind "java.lang.Throwable"

Note that these bindings imply a lot of other indirectly referred bindings which are present in the standard library.

In order to base bindings on existing ones other then those of the standard library, a binding file may contain a special directive referring to the directory of an OCS subsystem generated by javabind:

system "JavaExtendedLib"
  uses "./JavaLib"
  bind "..."

If an application uses several binding libraries, it is important that they are hierarchically built as sketched above. Otherwise bindings for the same class would be generated twice, leading to multiply defined symbol errors at link time.

A special instruction in a javabind file allows a group of structures to be clustered in a single structure which re-exports them. For example:

system "JavaLib"
  // bind a lot of stuff from java.io
  ...
  // cluster the structures generated for the java.io classes
  cluster "java.io.*" in "java_io"

Subsystem Hierarchy

 Subsystem JavaVM
 Subsystem JavaCheck


next node: Subsystem JavaVM,
prev node: Tk,
up to node: Subsystem Tools